Quick-Win Fixes: Code Style, Logging Helpers, and Time Portability#193
Conversation
Fixes Issue #159 Added PSTR(bs) helper macro to logging.h for cleaner Pascal string logging: - Wraps stringbaseaddress(bs) for improved readability - Documented with usage examples - Makes Pascal string logging more consistent across codebase Before: log_debug(LOG_COMP_HASH, "name: %s", stringbaseaddress(bs)); After: log_debug(LOG_COMP_HASH, "name: %s", PSTR(bs)); The macro is available but not yet applied to existing code. Future commits can convert existing stringbaseaddress() calls to use PSTR().
Fixes Issue #167 (P0 Bug) Changes: 1. Added frontier_time_t typedef (int64_t) in timedate.h - Explicit 64-bit time representation (not platform-dependent) - Seconds since 1904-01-01 00:00:00 UTC (Frontier epoch) - Solves 2040 overflow and cross-platform compatibility 2. Updated table_context.h to use frontier_time_t instead of time_t - Replaced time_t last_mutation_time with frontier_time_t - Updated documentation to clarify Frontier epoch usage 3. Updated table_context.c to convert Unix time to Frontier time - Converts time(NULL) result to Frontier epoch before storing - Adds FRONTIER_EPOCH_TO_UNIX_OFFSET (2,082,844,800 seconds) Rationale: - time_t is 32-bit on some platforms, 64-bit on others (not portable) - Using explicit int64_t ensures consistent behavior across platforms - Maintains compatibility with Frontier's 1904 epoch standard - Prevents 2040 overflow bug (32-bit unsigned wraps in 2040) See: planning/phase3/date_time_format_standard.md Related: Issue #167, date/time portability audit
Code Review - PR #193: Quick-Win FixesI've reviewed this PR consolidating three quick-win fixes. Overall, the changes are well-structured and properly documented. Here's my detailed feedback: ✅ APPROVED - High Quality ImplementationAll three fixes are solid and follow project conventions. I have only minor suggestions for consideration. Issue #171: Code Style StandardizationStatus: ✅ Excellent
Feedback: Perfect cleanup, no issues. Issue #159: Pascal String Logging HelperStatus: ✅ Good with one consideration What works well:
Consideration: The PSTR() macro wraps #define stringbaseaddress(bs) (bs+1)Potential issue: The comment in logging.h states:
However, Recommendation: Update the comment to reflect the actual dependency: /**
* Note: The macro requires standard.h to be included (for stringbaseaddress()).
* Since standard.h is included by most source files, this should work automatically.
*/Alternatively, you could leave it as-is since the dependency is satisfied in practice. This is a minor nit and doesn't block merge. Issue #167: Time Portability Fix (P0 Bug)Status: ✅ Excellent - Critical fix properly implemented What works well:
Cross-platform verification:
Feedback: This is a textbook example of fixing a portability bug. Well done. Testing VerificationRequired before merge: # Run full headless test suite
./tools/run_headless_tests.sh
# Verify database migration with new timestamp handling
make -C tests clean && make -C tests save_migration_tests
./tests/save_migration_testsThe PR description mentions these tests are needed but doesn't confirm they've been run. Please confirm test results before merge. Code Quality Assessment
Security Review✅ No security concerns identified
Performance Considerations✅ No performance impact
RecommendationsBefore Merge (Required):
Follow-up (Optional):
Final Verdict✅ APPROVED FOR MERGE (pending test confirmation) This PR demonstrates excellent engineering practices:
Risk Level: ✅ Low - All changes are isolated and well-understood Great work consolidating these quick wins! 🚀 References
|
Code Review: PR #193 - Quick-Win FixesOverall AssessmentAPPROVE WITH MINOR RECOMMENDATIONS ✅ This PR consolidates three well-scoped fixes addressing code style, logging infrastructure, and a critical portability bug. The changes are clean, well-documented, and align with project standards. Minor documentation improvement recommended before merge. Issue #171: Code Style Standardization ✅Files: Changes: Standardized 6 pointer casts from Assessment:
Recommendation: APPROVE Issue #159: Pascal String Logging Helper
|
…ntext.h **Problem 1: memory.c function redefinitions (FIXED)** Test builds define FRONTIER_USE_PORTABLE_HANDLES and include both: - memory_portable.h (inline stubs for portable handle API) - memory.c (real implementations for Classic handles) This caused 13 redefinition errors for: lockhandle, unlockhandle, newhandle, newemptyhandle, disposehandle, gethandlesize, sethandlesize, minhandlesize, moveleft, moveright, clearbytes, copyhandle, texthandletostring. **Solution**: Guard these functions in memory.c with `#ifndef FRONTIER_USE_PORTABLE_HANDLES` so they're excluded when portable stubs are active. This allows tests to use the inline memory_portable.h stubs while production code uses the real memory.c implementations. **Problem 2: table_context.h includes timedate.h but boolean not defined (FIXED)** Recent change added `#include "timedate.h"` to table_context.h. When included in test context, the `boolean` type used by timedate.h wasn't yet defined, causing multiple "unknown type name 'boolean'" errors. **Solution**: Move the guarded `typedef unsigned char boolean` to BEFORE the `#include "timedate.h"` directive. This ensures boolean is defined before timedate.h needs it, while avoiding redefinition warnings via the `#ifndef boolean` guard. **Test Results**: - Before fix: 13 function redefinition errors + multiple boolean errors - After fix: 0 redefinition errors, 0 boolean errors - Remaining errors are pre-existing (hdlintarray, quickdraw.h, etc.) Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Code Review: PR #193 - Quick-Win FixesI've reviewed this PR which bundles three distinct fixes (Issues #171, #159, and #167). Overall, this is solid work with good documentation and clear intent. Here's my detailed feedback: ✅ Strengths1. Issue #171 - Code Style Standardization
2. Issue #167 - Time Portability Fix ⭐
3. Issue #159 - PSTR() Logging Helper
4. General Quality
|
…ons to timedate.h Problem 1: memory.c array functions using undefined hdlintarray - Functions newintarray, debugnewintarray, setintarray, getintarray, fillintarray - Use hdlintarray which is a handle (short **) in non-portable builds - But hdlintarray is a struct in portable builds (portable/shelltypes_portable.h) - These functions dereference hdlintarray, which only works with pointer types Fix 1: - Wrap newintarray (line 2217) and debugnewintarray (line 1964) in #ifndef FRONTIER_USE_PORTABLE_HANDLES - Wrap setintarray, getintarray, fillintarray (lines 2232-2282) in same guard - These functions are excluded from portable builds where hdlintarray is not a handle Problem 2: timedate.h missing type definitions - Header declares functions using bigstring and boolean types - But does not include standard.h where these are defined - Including standard.h creates circular dependency issues Fix 2: - Add forward declarations of bigstring and boolean in timedate.h - Following same pattern as table_context.h (avoid circular dependencies) - bigstring defined as unsigned char[256] (matches Str255 definition) - boolean defined as unsigned char Verification: Compilation errors reduced from 19 to 6 (only quickdraw.h errors remain, not in scope) Addresses issues #171, #159, #167
Code Review - PR #193: Quick-Win FixesI've completed my review of this PR addressing Issues #171, #159, and #167. Overall, this is high-quality work with clean implementations, good documentation, and proper alignment with project standards. Here's my detailed feedback: ✅ APPROVED - Strengths1. Issue #171: Code Style StandardizationStatus: ✅ Perfect
2. Issue #159: PSTR() Logging HelperStatus: ✅ Well-Designed Strengths:
Verified Usage Pattern:
This helper macro will provide real value when gradually applied across these files. Minor Note: The macro requires 3. Issue #167: Time Portability Fix (P0 Bug)Status: ✅ Critical Fix, Excellent Implementation What This Fixes:
Implementation Quality: ✅ Type Safety:
✅ Epoch Conversion:
✅ Documentation:
✅ Header Dependencies:
4. Memory.c Guards - Portable Handle SupportStatus: ✅ Correct Changes:
Verification:
Good Documentation: /*
* 2025-12-28 Codex: Guard handle manipulation functions when using portable handles.
* When FRONTIER_USE_PORTABLE_HANDLES is defined, memory_portable.h provides inline
* stubs for these functions. This avoids redefinition errors in test builds.
*/5. CLAUDE.md Updates - Multi-Session Stability PatternsStatus: ✅ Valuable Documentation Added 114 lines of guidance on working across multiple terminal sessions:
Why This Matters:
Quality: Well-structured, practical, and clearly written. This will save future developers (and Claude agents) from common pitfalls. 6. headless_mac_compat.c CleanupStatus: ✅ Dead Code Removal Removed 8 lines of dead code:
These were legacy stubs from early headless work that are no longer called. Removal is safe and improves code hygiene. 📋 Testing RecommendationsSuggested Test Plan (Before Merge):
Expected Outcomes:
🎯 Impact AssessmentCritical Bug Fix (Issue #167):Impact: 🔴 HIGH PRIORITY This fixes a P0 portability bug that would cause:
Recommendation: This should be merged ASAP to prevent future data corruption. Code Quality Improvements (Issues #171, #159):Impact: 🟢 Low-Risk, High-Value
🚀 RecommendationAPPROVE and MERGE after CI passes. This PR represents exactly the kind of "quick-win" work that improves codebase quality without introducing risk:
Compliments to the implementation:
📝 Post-Merge Follow-Up (Optional)Consider these incremental improvements in future PRs:
Overall Assessment: High-quality, well-documented, critical bug fix with bonus code quality improvements. Strongly recommend merge. 🎉 |
Code Review - PR #193: Quick-Win FixesSummaryThis PR consolidates three independent fixes addressing code style consistency (Issue #171), logging infrastructure (Issue #159), and critical portability bugs (Issue #167). The changes are well-structured and align with project standards. ✅ Strengths1. Issue #167 - Time Portability Fix (P0 Bug)EXCELLENT implementation that correctly follows the documented standard in
This is a critical fix that prevents data corruption on 32-bit platforms and ensures timestamp consistency across all builds. 2. Issue #159 - PSTR() Logging HelperGOOD quality-of-life improvement:
3. Issue #171 - Code Style StandardizationGOOD consistency fix:
4. Build Fix - memory.c and test compilationNECESSARY and correctly implemented:
5. Logging Standards Compliance
|
Fixes bot feedback: timedate.h declares frontier_time_t as int64_t but was missing the necessary stdint.h include. While this worked due to transitive includes in current code, it violates header self-sufficiency principle. Addresses blocking issue in PR #193 code review.
|
Fixed the blocking issue: Added missing #include <stdint.h> to Common/headers/timedate.h (commit ff5ba8b). The header now properly includes stdint.h for the int64_t typedef, ensuring header self-sufficiency. PR is ready for re-review. |
Code Review - PR #193Overall Assessment ✅This PR successfully addresses three distinct issues with clean, focused changes. The code quality is high, and the changes align well with project standards documented in CLAUDE.md. Detailed Review by Issue✅ Issue #171: Code Style Standardization (langexternal.c)Changes: Standardized 6 pointer casts from Assessment:
Observation: The changes are in log_trace() calls, which have zero runtime cost when disabled. Good choice for style improvements. ✅ Issue #159: Pascal String Logging Helper (
|
Convert 10 high-churn logging statements from stringbaseaddress(bs) to PSTR(bs) to demonstrate the improved readability: - langexternal.c: 5 conversions (langexternalgettable function) - langstartup.c: 1 conversion (newfunctionprocessor function) - langvalue.c: 3 conversions (kernelfunctionvalue function) - langops.c: 3 conversions (langfindsymbol function) This incremental adoption demonstrates the PSTR() macro's value without requiring a mass conversion across the codebase. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Create comprehensive user-facing documentation explaining: - What frontier_time_t is (int64_t, 1904 epoch) - Why it was introduced (P0 bug: 32-bit time_t overflows in 2038) - How it differs from Unix time_t (epoch, size, portability) - Migration notes for developers (when to use, how to convert) - Code examples for storing/retrieving/comparing timestamps - Testing considerations for time portability This documentation helps developers understand the time portability fix and provides clear guidance for working with timestamps in Frontier's database format. Related: Issue #159, PR #193 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Create comprehensive test suite verifying: - frontier_time_t is always 64-bit (8 bytes) - Epoch conversion between Unix (1970) and Frontier (1904) works correctly - Timestamps survive round-trip conversions - Year 2038 boundary case works (no 32-bit overflow) - Byte order operations preserve values - Time range covers very old (1904) and far future (3000+) dates - Arithmetic operations (addition, subtraction, comparison) work correctly All 19 tests pass on modern 64-bit systems, demonstrating that frontier_time_t provides portability and future-proofing. Related: Issue #159, PR #193 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Code Review - PR #193: Quick-Win FixesI've reviewed this PR addressing Issues #171, #159, and #167. Here's my analysis: ✅ Strengths1. Issue #167 (Time Portability) - Critical Fix
2. Issue #171 (Code Style) - Consistent Application
3. Issue #159 (Logging Helper) - Good Design
4. CLAUDE.md Updates
5. Memory Management Guards
|
…ment Addresses bot feedback: PSTR() macro requires stringbaseaddress() which is declared in strings.h. By including strings.h in logging.h, the PSTR() macro becomes self-contained and can be used in any context without requiring specific include order.
Addresses bot feedback on documentation discoverability: - Added 'See Also' section to frontier_time_t_standard.md with links to related files - Enhanced timedate.h header comment to reference both developer guide and architectural context This helps developers discover the time portability standard when working with timestamps or reviewing time-related code.
Post-Merge Code Review: PR #193Note: This PR has already been merged. This review is provided for documentation and continuous improvement. Overall Assessment ✅This PR successfully consolidates three quick-win fixes with good separation of concerns and comprehensive testing. The changes are well-documented and follow project conventions. Detailed Feedback1. Issue #171 - Code Style Standardization ✅Changes: Standardized pointer cast formatting from Positives:
Observations:
2. Issue #159 - Pascal String Logging Helper ✅Changes: Added Positives:
Minor Observations:
3. Issue #167 - Time Portability Fix (P0 Bug) ✅ CRITICAL FIXChanges: Replaced platform-dependent Positives:
Architecture Notes:
Potential Considerations:
4. Memory.c Guards ✅Changes: Added Positives:
Observations:
5. CLAUDE.md Documentation Updates ✅Changes: Added 114 lines documenting multi-session stability patterns. Positives:
Value:
Test Coverage Assessment ✅Test File: Coverage:
Quality: Excellent - comprehensive coverage of edge cases and portability concerns. Security Considerations ✅No security concerns identified:
Performance Considerations ✅Impact: Negligible
Suggested Follow-ups (Not Blocking)
Alignment with CLAUDE.md Standards ✅
ConclusionRating: ⭐⭐⭐⭐⭐ (5/5) This is a high-quality PR that addresses three distinct issues with appropriate separation, comprehensive testing, and excellent documentation. The time portability fix (#167) is particularly important as it prevents a critical 2038 overflow bug and ensures consistent cross-platform behavior. The documentation quality ( Recommendation: ✅ Already merged - no concerns with merge decision. Reviewed by: Claude Sonnet 4.5 (Automated Code Review) |
…tore CLI functionality **Problem**: frontier-cli segfaulted immediately on execution after PR #193 (abc0d5e) **Root Cause**: PR #193 added `#ifndef FRONTIER_USE_PORTABLE_HANDLES` guards around critical memory functions (newfilledhandle, lockhandle, unlockhandle, etc.) to fix test redefinition errors. However, these guards excluded these functions when frontier-cli was built with -DFRONTIER_USE_PORTABLE_HANDLES, causing: - newfilledhandle, lockhandle, unlockhandle, validhandle → undefined at link time - `-Wl,-undefined,dynamic_lookup` linker flag masked the missing symbols - Runtime crash with NULL pointer dereference when calling undefined functions **Stack trace before fix**: ``` #0 0x000000000000 (NULL function pointer) #1 newheapvalue langvalue.c:358 #2 setstringvalue langvalue.c:379 #3 langaddstringconst langstartup.c:571 ``` **Solution**: 1. Remove ALL `#ifndef FRONTIER_USE_PORTABLE_HANDLES` guards from memory.c - Restores lockhandle, unlockhandle, validhandle, newfilledhandle, etc. - Returns memory.c to state after PR #189 (7c72906) which removed guards 2. Add missing `validhandle()` inline stub to portable/memory_portable.h - memory_portable.h had lockhandle/unlockhandle but was missing validhandle - Prevents future undefined symbol errors in portable builds 3. Add tools/bisect_test_cli_basic.sh for future git bisect debugging **Test redefinition errors**: PR #193 added these guards to fix test compilation redefinition errors. Those errors will need to be addressed differently (likely by improving the test build configuration to avoid dual compilation of memory functions). **Verification**: ``` FRONTIER_HEADLESS_SKIP_STARTUP=1 ./frontier-cli/frontier-cli -e "1+1" Result: 2 # ✓ SUCCESS ``` Fixes segfault introduced by PR #193 (commit abc0d5e) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit documents the requirement to audit new source files for uint32_t timestamp usage before adding them to headless builds, to ensure the 64-bit time migration (frontier_time_t) is complete. Key additions: 1. CLAUDE.md - New section "Timestamp Type Migration - uint32_t Audit Required": - Pre-merge checklist for auditing new files - Distinguishes between disk format (OK) vs in-memory state (must migrate) - Example patterns showing correct conversion - References wptext_runtime.c as known issue for Phase 3 2. planning/phase3/uint32_timestamp_audit.md - Complete audit report: - Found wptext_runtime.c uses 'long' instead of frontier_time_t (CRITICAL) - Confirmed langhash.c uint32_t usage is OK (legacy disk format only) - Confirmed timedate.c uint32_t usage is OK (Mac-only code path) - Provides action plan for Phase 3 fix This was discovered during PR #231 when timet_to_frontierseconds() was found to be returning uint32_t instead of frontier_time_t, truncating timestamps. References: - docs/frontier_time_t_standard.md - 64-bit time standard - Issue #167 - Original time_t portability bug - PR #193 - Implementation of frontier_time_t - PR #231 - File verb bindings (where issue was discovered) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…231) * feat: Implement file verbs dispatcher with _Static_assert pattern Implemented complete dispatcher for all 86 file verbs following the established pattern from string and table verbs. Changes: - tests/headless_file_verbs.c: Complete dispatcher implementation - Token enum with all 86 file verbs (0-85) - Compile-time _Static_assert verification (count = 86) - Dispatcher function forwarding to filefunctionvalue() - Full fileinitverbs() registering all 86 verbs - Common/source/fileverbs.c: Made filefunctionvalue() extern - Removed static keyword to allow dispatcher to call it - Line 2573 - tests/integration/test_cases/file_verbs.yaml: TDD integration tests - 12 tests covering portable POSIX file operations - fileFromPath, exists, new, writeWholeFile, readWholeFile, delete Dispatcher pattern verified: - _Static_assert fails correctly with wrong count (85) - _Static_assert passes with correct count (86) - Build succeeds, file processor registers Known issue: File verb implementations have Mac-specific dependencies (macgetfilespecnameasbigstring) causing segfaults. Needs platform abstraction work similar to table/string verbs. Test results: 2/12 pass (only error cases that don't reach impl) Related: PR #221 (string verbs), PR #224 (table verbs) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: Add portable implementations of Mac file functions Added portable C implementations for Mac-specific file operations to enable file verb execution in headless mode. Implemented functions: - endswithpathsep(bigstring) - Check if path ends with separator - Mac: checks for ':' - Portable: uses chpathseparator ('/' on POSIX) - filefrompath(bigstring, bigstring) - Extract filename from path - Calls lastword() with chpathseparator - Example: "/tmp/test.txt" → "test.txt" - macgetfilespecnameasbigstring(ptrfilespec, bigstring) - Uses existing filespectopath() from portable/file_portable.c - Accesses fs->name.unicode directly (portable structure) Pattern: Similar to Windows port approach - replace Mac FSRef/HFS operations with vanilla C using portable structures (PortableUniStr255). Status: Builds successfully, but file verb tests still fail with segfaults. Additional Mac dependencies need investigation. Related: Commit 318189d8 (file verbs dispatcher) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: Implement thread-local working directory system (Phase 1) Implements foundational infrastructure for thread-isolated working directories in headless mode. Each UserTalk thread now has its own current working directory that can be changed independently without affecting other threads. Architecture: - Process-level: g_process_default_cwd initialized from getcwd() at startup - Thread-level: current_working_directory field in tythreadglobals - New threads inherit process default automatically - Thread context switching preserves cwd (existing swap mechanism) Changes: - Common/headers/processinternal.h: Add current_working_directory to tythreadglobals - portable/file_working_dir.h: New API for init/get/set working directory - portable/file_working_dir.c: Implementation with logging and validation - Common/source/process.c: Initialize thread cwd from process default - frontier-cli/main.c: Initialize process cwd from getcwd() with fallbacks - portable/file_portable.c: Add filegetdefaultpath/filesetdefaultpath implementations - frontier-cli/Makefile: Add file_working_dir.c to build Rationale: This enables file.getPath() and file.setPath() to work correctly in multi-threaded headless environments. Supports future collaborative ODB editing where multiple users edit concurrently with isolated working directories. Note: file.getPath()/file.setPath() verbs cannot be tested yet because fileverbs.c (86 file verbs) needs porting to headless mode. Phase 1 provides the infrastructure; verb implementations are Phase 2. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: Add portable file verb infrastructure (Phase 1 - Infrastructure Setup) Implements delegation infrastructure for 86 file verbs in headless mode. All verbs now have portable stub implementations that return clear error messages instead of segfaulting. Changes: - portable/fileverbs_portable.h: API header for portable file verbs - portable/fileverbs_portable.c: Dispatcher with 86 verb stubs - tests/headless_file_verbs.c: Updated delegation to use portable_filefunctionvalue() - frontier-cli/Makefile: Added fileverbs_portable.c to HEADLESS_STUBS Verb categorization (from system architect plan): - Tier 1 (Critical): 20 verbs - file ops, paths, creation (TODO Phase 2) - Tier 2 (File I/O): 14 verbs - handles, read/write (TODO Phase 3) - Tier 3 (Optional): 16 verbs - locking, visibility (TODO Phase 4) - Tier 4 (Mac-specific): 36 verbs - UI dialogs, metadata (STUBBED with clear errors) Testing: - Build succeeds: `make -C frontier-cli` - Delegation works: file.exists() returns "not implemented" error (not segfault) - Mac-specific verbs return clear user-friendly error messages Next steps: Implement Tier 1 critical operations (file.exists, file.delete, file.rename, file.copy, file.getpath, file.setpath, etc.) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: Implement Tier 1 critical file verbs (Phase 2 Milestone 1 - 9/20 verbs) Implements 9 of 20 Tier 1 critical file operations using POSIX APIs. All verbs tested and working in headless mode. Implemented verbs: - file.getpathchar() - Returns '/' path separator - file.exists(path) - Check file/folder existence (stat) - file.isfolder(path) - Check if path is directory (S_ISDIR) - file.isvolume(path) - Always returns false (no Mac volumes in headless) - file.size(path) - Return file size in bytes (st_size) - file.created(path) - Return creation date (st_birthtime on macOS, st_ctime fallback) - file.modified(path) - Return modification date (st_mtime) - file.fullpath(path) - Resolve to absolute path (realpath) - file.getpath() - Get current working directory (thread-local or process default) - file.setpath(path) - Set current working directory (thread-local or process default) Key features: - Thread-local working directory fully functional end-to-end - Graceful fallback to process default when no thread context (CLI mode) - Unix time_t correctly converted to Frontier time (seconds since 1904) - Helper function: filespec_to_cstring() for path extraction Changes: - portable/fileverbs_portable.c: Implemented 9 verb cases with POSIX stat/realpath - portable/file_working_dir.c: Fixed set_thread_working_dir() to write process default in CLI mode Testing: - file.exists("databases/Frontier-v6.root") → true - file.size("databases/Frontier-v6.root") → 6099497 - file.modified("databases/Frontier-v6.root") → "12/31/2025; 11:45:31 PM" - file.setpath("databases"); file.getpath() → "databases" Remaining Tier 1 verbs (11/20): - file.filefrompath(), file.folderfrompath() - file.new(), file.newfolder() - file.delete(), file.rename() - file.copy(), file.move() - file.getsystemfolderpath(), file.getspecialfolderpath() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: Implement all 20 Tier 1 critical file verbs (Phase 2 Milestone 2) Implements the remaining 11 Tier 1 file verbs using POSIX APIs: **Path manipulation (2 verbs):** - file.filefrompath() - Extract filename from path - file.folderfrompath() - Extract folder path from full path **File/folder creation (2 verbs):** - file.new() - Create empty file - file.newfolder() - Create directory with 0755 permissions **File modification (4 verbs):** - file.delete() - Delete file or folder (uses unlink/rmdir) - file.rename() - Rename file or folder - file.copy() - Streaming copy with permission preservation - file.move() - Efficient rename, falls back to copy+delete for cross-volume **Special paths (2 verbs):** - file.getsystemfolderpath() - Map OSType codes to Unix paths - file.getspecialfolderpath() - Same implementation as getsystemfolderpath() **Bug fixes:** - Fixed file.isvolume() NULL pointer crash - Fixed BIGSTRING length for getsystemfolderpath (19 chars, not 20) - Fixed BIGSTRING length for getspecialfolderpath (20 chars, not 21) **Testing:** All 20 Tier 1 verbs tested and working: ✓ file.getpathchar(), exists(), isfolder(), isvolume(), size() ✓ file.created(), modified(), fullpath(), getpath(), setpath() ✓ file.filefrompath(), folderfrompath() ✓ file.new(), newfolder(), delete(), rename() ✓ file.copy(), move() ✓ file.getsystemfolderpath(), getspecialfolderpath() Implementation progress: 20 of 86 file verbs (23% complete) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: Implement 12 of 14 Tier 2 file I/O verbs (Phase 2 Milestone 3) Implements file handle management and I/O operations using POSIX FILE* API. **File Handle Infrastructure:** - File handle table (MAX_OPEN_FILES=64) with refnum allocation - Helper functions: allocate_filehandle(), get_filepointer(), release_filehandle() **Implemented Verbs (12 working):** ✅ **Handle Management (2 verbs):** - file.open(filespec, [readonly]) - Opens file, returns refnum - file.close(refnum) - Closes file and releases handle ✅ **Position/EOF Operations (5 verbs):** - file.endoffile(refnum) - Check if at EOF - file.setendoffile(refnum) - Truncate at current position - file.getendoffile(refnum) - Get file size - file.setposition(refnum, pos) - Seek to position - file.getposition(refnum) - Get current position ✅ **Line I/O (1 verb):** - file.readline(refnum) - Read line (handles CR/LF/CRLF) ✅ **Binary I/O (1 verb):** - file.read(refnum, count) - Read bytes (string if ≤255, binary otherwise) ✅ **Whole File I/O (1 verb):** - file.readwholefile(filespec) - Read entire file (string/binary) ✅ **Utility (2 verbs):** - file.compare(file1, file2) - Returns true if identical 🎉 - file.countlines(filespec) - Not implemented (stub) - file.findinfile(filespec, string) - Not implemented (stub) **Known Issues (2 verbs need debugging):** - file.writeline(refnum, string) - "too many parameters" error - file.write(refnum, data) - "too many parameters" error - file.writewholefile(filespec, data) - "Data must be string or binary" error Issue is with getexempttextvalue() parameter handling - will fix in follow-up. **Testing:** ✓ file.open/close/getposition/setposition - Working ✓ file.read(refnum, 100) returns 100 bytes - Working ✓ file.readline() reads first line - Working ✓ file.compare(file, file) returns true - Working 🎉 ✓ file.readwholefile() returns file contents - Working **Progress:** 32 of 86 file verbs implemented (37% complete) - Tier 1: 20/20 verbs ✅ (100%) - Tier 2: 12/14 verbs ✅ (86%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: Complete Tier 2 file I/O verbs (14/14 working) Implemented all 14 Tier 2 file I/O operations: - openfile() - Open file, returns refnum - closefile() - Close file by refnum - endoffile() - Check EOF status - setendoffile() - Truncate file at current position - getendoffile() - Get file size - setposition() - Seek to position - getposition() - Get current position - readline() - Read line (handles CR/LF) - writeline() - Write line with newline - read() - Read bytes (returns string or binary) - write() - Write bytes from string - readwholefile() - Read entire file - writewholefile() - Write entire file - compare() - Byte-by-byte file comparison **Implementation Details:** - File handle table (64 slots) maps refnum to FILE* - POSIX file I/O (fopen, fread, fwrite, fseek, ftell, fclose) - String/binary handling: ≤255 bytes = string, >255 = binary - Fixed global state bug: flnextparamislast reset per verb call **Critical Fix:** Added flnextparamislast=false at start of portable_filefunctionvalue() to prevent parameter counting errors when verbs are chained (e.g., file.open() followed by file.writeline()). This global variable must be reset on each verb call to avoid state leakage. **Progress:** - Tier 1: 20/20 verbs ✓ (critical file operations) - Tier 2: 14/14 verbs ✓ (file I/O) - Total: 34/86 file verbs (40% complete) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * test: Add parameter state isolation test for file verbs Documents the band-aid fix for flnextparamislast global state leakage when chaining file verb calls (e.g., file.open followed by file.writeline). This test ensures parameter state is properly reset between verb calls, preventing "too many parameters" errors in verb chains. Note: This is a temporary fix until proper thread-local storage migration (ADR-005) is implemented to eliminate global state dependencies. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: Add atexit() cleanup hook for file handles Implements automatic file handle cleanup on process exit to: 1. Flush unflushed FILE* buffers (prevents data loss) 2. Detect and log file handle leaks (debugging aid) 3. Ensure proper resource cleanup (RAII pattern) Implementation: - cleanup_file_handles() closes all open file handles - ensure_cleanup_registered() registers atexit() on first file.open() - Logs warning with leak count when files not explicitly closed Technical Details: - atexit() guarantees cleanup on normal exit (exit, return from main) - Does NOT run on SIGKILL (uncatchable) or _exit() (rare) - OS still closes FDs and releases locks on crash - Primary benefit: buffer flushing and leak detection Testing: - Verified warning logs when files left open (2-3 leaked files) - Verified silence when files properly closed - fclose() flushes buffers before exit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Fix three critical file handle issues identified in PR review 1. Refnum overflow after 32,767 opens - Implemented refnum reuse in allocate_filehandle() - Prevents negative refnums and duplicate assignments - Handles wrap-around by searching for unused refnums 2. Race condition in atexit() registration - Moved cleanup registration from lazy init to main() startup - Prevents double-registration when two threads open files simultaneously - Renamed ensure_cleanup_registered() to init_file_handle_cleanup() - Made function public and called before any threading 3. ferror() after fclose() undefined behavior - Moved ferror() check before fclose() in filemovefunc - Prevents calling ferror() on closed FILE* stream - Maintains proper error detection during cross-volume moves All fixes tested with full test suite - all tests passing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Fix 5 bot-identified blocking issues for PR #231 Addresses all critical/medium issues from code review bot: 1. Buffer overflow fix (CRITICAL - Security) - Reordered readline() condition to check len < 255 before fgetc() - Prevents reading character that won't be stored - Avoids potential buffer overrun at boundary condition 2. Error message correction (MEDIUM) - Changed 'Write error during copy' to 'Read error during copy' - Error message now correctly reflects ferror(fpsrc) check 3. Thread-safe file handle table (HIGH - Architectural) - Added pthread_mutex_t to protect global filetable array - Wrapped all table operations with mutex lock/unlock: * allocate_filehandle() - refnum allocation * get_filepointer() - refnum lookup * release_filehandle() - refnum release * cleanup_file_handles() - cleanup iteration - Enables safe concurrent file operations from multiple threads 4. Startup assertion (MEDIUM) - Added assert() to init_file_handle_cleanup() - Ensures function only called once before threading - Prevents double-registration of atexit() hook 5. Binary data support in writewholefile() (MEDIUM) - Changed from getstringvalue() to getexempttextvalue() - Now handles both string and binary (Handle) data - Properly locks/unlocks handle during write - Disposes handle on all paths (success/error) - Fixes integration test failures All blocking issues resolved. Tests passing. Ready for re-review. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Fix 4 additional bot-identified issues for PR #231 Addresses all 'Must Fix Before Merge' items from second bot review: 1. CRITICAL - Mutex usage in cleanup (race condition prevention) - Moved fclose() calls outside mutex-protected section - Two-phase cleanup: collect FILE* under lock, close without lock - Prevents holding mutex during blocking I/O operations - Eliminates deadlock risk on network filesystems 2. HIGH - Buffer overflow in readline (off-by-one fix) - Changed from post-increment to pre-increment pattern - Now uses: bsline[++len] instead of bsline[len+1]; len++ - Ensures maximum 255 bytes written (indices 1-255) - Prevents potential overflow at boundary condition 3. MEDIUM - chmod() error handling (permission preservation) - Added error checking for chmod() in file.copy and file.move - Logs warning if permission preservation fails - Continues operation (copy succeeded even if chmod failed) - Improved debugging for permission-related issues 4. MEDIUM - next_refnum overflow (immediate wrap) - Added wrap check immediately after increment - Prevents next_refnum from staying negative - Pattern: next_refnum = candidate + 1; if (<=0) wrap to 1 - Ensures refnum always stays in valid positive range All fixes tested. Full test suite passing. Ready for final review. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * refactor: Address minor code quality improvements from PR review This commit implements all three non-blocking improvements suggested in the code review: 1. Add comprehensive fseek() error checking: - Check return values in getendoffilefunc() - Check return values in readwholefilefunc() - Provide specific error messages for each failure case 2. Add 500MB file size limit to readwholefile(): - Define MAX_READWHOLEFILE_SIZE (500MB) constant - Validate file size before allocating memory - Prevents OOM on extremely large files 3. Simplify refnum allocation logic: - Replace complex wraparound algorithm with direct slot indexing - Use refnum = slot_index + 1 for O(1) allocation - Optimize get_filepointer() from O(n) to O(1) lookup - Remove unnecessary next_refnum global variable - Eliminate potential infinite loop scenario All changes maintain backward compatibility and pass full test suite. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: Address critical file handle management issues from code review This commit fixes two critical issues identified in the bot review: 1. Fix release_filehandle() race condition and optimize lookup: - Change from O(n) linear search to O(1) direct indexing - Defensively close FILE* in release_filehandle() to prevent leaks - Close file outside mutex to avoid blocking I/O under lock - Simplify closefilefunc() to rely on release_filehandle()'s fclose() 2. Add Y2038 overflow protection to timet_to_frontierseconds(): - Check for overflow before adding FRONTIER_EPOCH_OFFSET - Log warning and return UINT32_MAX on overflow - Prevents incorrect date conversion for dates after 2038 Both fixes maintain the established O(1) pattern used in allocate_filehandle() and get_filepointer(), ensuring consistent performance characteristics across all file handle operations. All tests pass (unit + integration). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: Use frontier_time_t (64-bit) for all timestamp conversions This commit corrects the timet_to_frontierseconds() implementation to align with the project's 64-bit time migration (docs/frontier_time_t_standard.md): 1. Change return type from uint32_t to frontier_time_t (int64_t) 2. Change local variables from uint32_t to frontier_time_t 3. Remove incorrect overflow check (not needed with 64-bit types) 4. Update file header to show Phase 2 completion (34/86 verbs) The previous implementation truncated 64-bit time_t values to 32-bit uint32_t, which defeats the purpose of the 64-bit time migration that ensures Frontier works correctly beyond the year 2038. Now all timestamp operations use the correct 64-bit frontier_time_t type, matching the signature expected by setdatevalue(int64_t). All tests pass (unit + integration). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * docs: Add uint32_t timestamp audit guidance and findings This commit documents the requirement to audit new source files for uint32_t timestamp usage before adding them to headless builds, to ensure the 64-bit time migration (frontier_time_t) is complete. Key additions: 1. CLAUDE.md - New section "Timestamp Type Migration - uint32_t Audit Required": - Pre-merge checklist for auditing new files - Distinguishes between disk format (OK) vs in-memory state (must migrate) - Example patterns showing correct conversion - References wptext_runtime.c as known issue for Phase 3 2. planning/phase3/uint32_timestamp_audit.md - Complete audit report: - Found wptext_runtime.c uses 'long' instead of frontier_time_t (CRITICAL) - Confirmed langhash.c uint32_t usage is OK (legacy disk format only) - Confirmed timedate.c uint32_t usage is OK (Mac-only code path) - Provides action plan for Phase 3 fix This was discovered during PR #231 when timet_to_frontierseconds() was found to be returning uint32_t instead of frontier_time_t, truncating timestamps. References: - docs/frontier_time_t_standard.md - 64-bit time standard - Issue #167 - Original time_t portability bug - PR #193 - Implementation of frontier_time_t - PR #231 - File verb bindings (where issue was discovered) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: Add reference counting to prevent FILE* race condition This commit implements reference counting for FILE* handles to prevent the TOCTOU (Time-of-Check-Time-of-Use) race condition identified in code review. Problem: -------- Thread A: get_filepointer(5) -> returns fp -> RELEASES MUTEX Thread B: release_filehandle(5) -> closes fp Thread A: fgetc(fp) -> USE-AFTER-FREE CRASH Root cause: Mutexes protected the file table, but not the FILE* lifetime. Once a FILE* was returned, another thread could close it at any time. Solution: --------- 1. Add refcount field to filehandle struct 2. get_filepointer() increments refcount under mutex 3. New release_filepointer() decrements refcount 4. release_filehandle() only closes when refcount == 0 5. If refcount > 0, mark for close and defer until last reference released Pattern: -------- fp = get_filepointer(refnum); // Increments refcount if (!fp) return false; /* Use fp safely - protected by refcount */ fgetc(fp); release_filepointer(refnum); // Decrements refcount, closes if last ref This is the same reference counting pattern recommended for external object contexts (Issue #135) for collaborative ODB support. Updated all 9 call sites: - endoffilefunc, setendoffilefunc, getendoffilefunc - setpositionfunc, getpositionfunc - readlinefunc, writelinefunc - readfunc, writefunc All tests pass (unit + integration). Addresses critical race condition from PR review. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Summary
This PR consolidates three quick-win fixes addressing code style consistency (Issue #171), logging infrastructure (Issue #159), and critical portability bugs (Issue #167). These changes improve code maintainability, provide logging conveniences, and ensure correct timestamp handling across platforms.
Key Changes
1. Issue #171: Code Style Standardization
File:
Common/source/langexternal.cStandardized pointer cast formatting from
(void*)to(void *)with consistent spacing. This aligns with code style feedback from PR #170 and ensures uniform formatting across logging statements.langexternalgettable()andlangnewexternalvariable()functions2. Issue #159: Pascal String Logging Helper
File:
Common/headers/logging.hAdded
PSTR()helper macro to improve readability when logging Pascal strings (bigstring type). This macro wraps thestringbaseaddress()function call, making code more concise and consistent.PSTR(bs)macro definition with comprehensive documentationlog_debug(..., stringbaseaddress(bs))→ Afterlog_debug(..., PSTR(bs))3. Issue #167: Time Portability Fix (P0 Bug)
Files:
Common/headers/timedate.h,Common/headers/table_context.h,Common/source/table_context.cReplaced platform-dependent
time_twith explicitfrontier_time_t(int64_t) to fix portability and prevent 2040 overflow bug.Changes:
frontier_time_ttypedef (int64_t) with documentationtime_t last_mutation_timewithfrontier_time_t<time.h>to explicit<stdint.h>and"timedate.h"table_context_record_mutation()to convert Unix time to Frontier epochFRONTIER_EPOCH_TO_UNIX_OFFSETconstant (2,082,844,800 seconds)Rationale:
time_tis 32-bit on some platforms, 64-bit on others (not portable)int64_tensures consistent behavior across platformsTesting
All three fixes have been implemented without introducing new dependencies or breaking changes:
Verification needed:
./tools/run_headless_tests.shto verify no regressionsIssues Addressed
Related Documentation
planning/phase3/date_time_format_standard.md- Date/time design standard