Skip to content

docs: capture headless daemon vision#21

Merged
jsavin merged 1 commit into
developfrom
docs/headless-daemon-vision
Oct 18, 2025
Merged

docs: capture headless daemon vision#21
jsavin merged 1 commit into
developfrom
docs/headless-daemon-vision

Conversation

@jsavin

@jsavin jsavin commented Oct 18, 2025

Copy link
Copy Markdown
Owner

No description provided.

@jsavin jsavin merged commit 9a4b411 into develop Oct 18, 2025
@jsavin jsavin deleted the docs/headless-daemon-vision branch October 18, 2025 01:01
jsavin added a commit that referenced this pull request Jan 11, 2026
… cursor invariants

This commit addresses outline operation test failures by properly documenting
and implementing the empty summit behavior, fixing op.firstSummit(), and
correcting test expectations to match UserTalk conventions and headless mode.

## Changes

### Documentation
- **docs/OUTLINE_STRUCTURE.md**: Added critical section documenting cursor and
  collapsed nodes invariant - the cursor can NEVER be on a child of a collapsed
  node. When expansion state is restored, cursor auto-moves to first visible
  ancestor if needed.

- **planning/phase3/OUTLINE_EMPTY_SUMMIT_FIX.md**: Documented the empty summit
  issue and historical context (file was created in previous work but uncommitted).

### Implementation Fixes
- **tests/headless_op_verbs.c**: Fixed op.firstSummit() to explicitly move to
  hsummit using opmoveto() instead of opmotionkey(flatup, infinity), and always
  return true (opmoveto returns false when already at target, causing test failures).

### Test Fixes
- **tests/integration/test_cases/op_verbs.yaml**:
  - Fixed test #11 (cursor independence): Rewrote to properly test that expansion
    state captures entire outline, accounting for cursor auto-move when parent is
    collapsed
  - Fixed type safety tests based on UserTalk conventions:
    - Test #19 (setCursor): Expects false when invalid cursor passed
    - Test #20 (setDisplay): UserTalk type coercion (1 → true) is correct behavior
    - Test #21 (setExpansionState): Expects json_parse_error for non-list
    - Test #22 (setScrollState): Noop in headless, returns true
    - Test #23 (setRefcon): Split into 4 tests proving round-trip for boolean,
      string, long, and double (refcons can store any type)

## Context

Following Phase 4 state management implementation, tests revealed misunderstandings
about:
1. Empty summit behavior (all outlines start with 1 empty headline)
2. Cursor visibility invariant (cursor must always be on accessible/visible node)
3. UserTalk type coercion vs strict type checking expectations

This commit documents these behaviors and aligns tests with reality.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
jsavin added a commit that referenced this pull request Jan 11, 2026
When passing a non-list type to op.setExpansionState(), it attempts type
coercion and fails with a script error: 'Can't coerce the string "foo" into
a number because it contains non-numeric characters.'

Verified in Windows Frontier by user testing.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
jsavin added a commit that referenced this pull request Jan 11, 2026
…anch

Reverting commits that were incorrectly pushed directly to develop:
- 2526b8b: Test #21 fix
- 85ddbc2: Outline operation test fixes
- 736d3cb: Phase 4 state management verbs

These changes will be properly submitted via PR workflow on a feature branch.
jsavin added a commit that referenced this pull request Jan 11, 2026
… cursor invariants

This commit addresses outline operation test failures by properly documenting
and implementing the empty summit behavior, fixing op.firstSummit(), and
correcting test expectations to match UserTalk conventions and headless mode.

- **docs/OUTLINE_STRUCTURE.md**: Added critical section documenting cursor and
  collapsed nodes invariant - the cursor can NEVER be on a child of a collapsed
  node. When expansion state is restored, cursor auto-moves to first visible
  ancestor if needed.

- **planning/phase3/OUTLINE_EMPTY_SUMMIT_FIX.md**: Documented the empty summit
  issue and historical context (file was created in previous work but uncommitted).

- **tests/headless_op_verbs.c**: Fixed op.firstSummit() to explicitly move to
  hsummit using opmoveto() instead of opmotionkey(flatup, infinity), and always
  return true (opmoveto returns false when already at target, causing test failures).

- **tests/integration/test_cases/op_verbs.yaml**:
  - Fixed test #11 (cursor independence): Rewrote to properly test that expansion
    state captures entire outline, accounting for cursor auto-move when parent is
    collapsed
  - Fixed type safety tests based on UserTalk conventions:
    - Test #19 (setCursor): Expects false when invalid cursor passed
    - Test #20 (setDisplay): UserTalk type coercion (1 → true) is correct behavior
    - Test #21 (setExpansionState): Expects json_parse_error for non-list
    - Test #22 (setScrollState): Noop in headless, returns true
    - Test #23 (setRefcon): Split into 4 tests proving round-trip for boolean,
      string, long, and double (refcons can store any type)

Following Phase 4 state management implementation, tests revealed misunderstandings
about:
1. Empty summit behavior (all outlines start with 1 empty headline)
2. Cursor visibility invariant (cursor must always be on accessible/visible node)
3. UserTalk type coercion vs strict type checking expectations

This commit documents these behaviors and aligns tests with reality.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
jsavin added a commit that referenced this pull request Jan 11, 2026
When passing a non-list type to op.setExpansionState(), it attempts type
coercion and fails with a script error: 'Can't coerce the string "foo" into
a number because it contains non-numeric characters.'

Verified in Windows Frontier by user testing.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
jsavin added a commit that referenced this pull request Jan 11, 2026
* fix: Phase 4 state management verbs - expansion state, refcon, and documentation

Fixed P0 expansion state save/restore issues and improved state management verbs:

- Fixed op.setExpansionState to restore state after collapse (tests 3, 7, 17)
- Root cause: Checked (**nomad).flexpanded flag which gets cleared by opcollapse()
- Solution: Only check !opsubheadsexpanded() to allow re-expansion
- Tests now passing: Basic restore, workflow restore, deep nesting stress test

- Fixed op.promote/demote to use oppromote()/opdemote() instead of opreorgcursor()
  - Properly handles multiple nodes and preserves refcon data (test 14)
- Fixed op.setCursor to use opmoveto() for proper state updates (test 15)
- Made scroll state operations noops in headless mode (tests 5, 13)

- Created docs/OUTLINE_STRUCTURE.md documenting critical architectural fact:
  All new outlines start with 1 empty summit headline
- Updated CLAUDE.md with outline structure quick reference
- Documented memory logging false alarms (format detection, not corruption)
- Created planning/phase3/phase4_test_failures.md analyzing all test failures

- Updated scroll state test expectations for headless mode behavior
- Fixed workflow test ordering issues

Test results: 537/600 passing (89.5%), P0 expansion state issues resolved

Related: Issues #272, #273 filed for future expansion state design improvements

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: Align outline operation tests with empty summit behavior and fix cursor invariants

This commit addresses outline operation test failures by properly documenting
and implementing the empty summit behavior, fixing op.firstSummit(), and
correcting test expectations to match UserTalk conventions and headless mode.

- **docs/OUTLINE_STRUCTURE.md**: Added critical section documenting cursor and
  collapsed nodes invariant - the cursor can NEVER be on a child of a collapsed
  node. When expansion state is restored, cursor auto-moves to first visible
  ancestor if needed.

- **planning/phase3/OUTLINE_EMPTY_SUMMIT_FIX.md**: Documented the empty summit
  issue and historical context (file was created in previous work but uncommitted).

- **tests/headless_op_verbs.c**: Fixed op.firstSummit() to explicitly move to
  hsummit using opmoveto() instead of opmotionkey(flatup, infinity), and always
  return true (opmoveto returns false when already at target, causing test failures).

- **tests/integration/test_cases/op_verbs.yaml**:
  - Fixed test #11 (cursor independence): Rewrote to properly test that expansion
    state captures entire outline, accounting for cursor auto-move when parent is
    collapsed
  - Fixed type safety tests based on UserTalk conventions:
    - Test #19 (setCursor): Expects false when invalid cursor passed
    - Test #20 (setDisplay): UserTalk type coercion (1 → true) is correct behavior
    - Test #21 (setExpansionState): Expects json_parse_error for non-list
    - Test #22 (setScrollState): Noop in headless, returns true
    - Test #23 (setRefcon): Split into 4 tests proving round-trip for boolean,
      string, long, and double (refcons can store any type)

Following Phase 4 state management implementation, tests revealed misunderstandings
about:
1. Empty summit behavior (all outlines start with 1 empty headline)
2. Cursor visibility invariant (cursor must always be on accessible/visible node)
3. UserTalk type coercion vs strict type checking expectations

This commit documents these behaviors and aligns tests with reality.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: Correct test #21 expectation - script_error not json_parse_error

When passing a non-list type to op.setExpansionState(), it attempts type
coercion and fails with a script error: 'Can't coerce the string "foo" into
a number because it contains non-numeric characters.'

Verified in Windows Frontier by user testing.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: Address Codex feedback issues #1, #3, #4

Issue #1 (Safety): Add pointer validation in op.setCursor()
- Validate nodeid is non-zero before casting to pointer
- Prevents potential null pointer dereference

Issue #2 (Default refcon): Already correctly returns 0
- op.getRefcon() returns 0 when hrefcon is nil (verified)
- No changes needed

Issue #3 (Documentation): Clarify Parent is sibling of empty summit
- Added explanatory comments in OUTLINE_STRUCTURE.md
- Explains op.insert("Parent", down) creates sibling, not child
- Both empty summit and Parent are at summit level

Issue #4 (Loop safety): Add explicit summit check
- Added summit termination check in expansion state restoration
- Checks both self-reference and hsummit comparison
- Prevents potential infinite loop

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
jsavin added a commit that referenced this pull request May 31, 2026
Investigation produced from reading /Users/jake/dev/tedchoward/Frontier/
Common/source/ (~9k lines across menu*.c, meprograms.c, langipcmenus.c,
tablestructure.{c,h}, cancoon.c). Triggered by JES guidance: editor
windows are coming, and the legacy menu system's windowTypes framework
(JES-authored UserTalk on top of kernel hooks) is the substrate they'll
plug into. We need to understand it before planning Phase 6 in detail.

Part 1 covers the C kernel side end-to-end:
- 3-layer architecture (kernel, UserTalk verb surface, UserTalk windowTypes framework)
- Data structures (menubarlist, menubarstack, menurecord)
- Database storage at system.menus.sharedmenus.<appid>
- Dispatch path from OS menu hit to script execution (memenu -> memenuhit -> meuserselected)
- Per-database menubar swap via setcancoonglobals
- Kernel->UserTalk callback registry (40+ idXxxScript hooks)
- The 13 menu.* UserTalk verbs
- Worked example: File > New Script click traced through to async script dispatch

Part 2 maps the legacy model to the headless port:
- What survives intact (data model, verb surface, dispatch mechanism)
- What needs adaptation (per-database scope, OS menu manager handoff)
- New substrate needed for editor windows (window-type registry, frontmost
  tracking, callback bridge for window events, multi-menubar enumeration)
- Proposed port sequencing in 5 steps
- Disposition of task #21 (File > Exit vs REPL > Exit) — answers itself once
  windowTypes drives composition (they appear at different times)

Gaps:
- The actual UserTalk windowTypes framework implementation lives in a
  Frontier.root not present in either tedchoward or jsavin/Frontier;
  framework contract here is inferred from kernel evidence + JES guidance
- idmenubarscript declared in tablestructure.h but unreferenced in source
  (vestigial or called from Mac-specific code outside Common/source)
- Async (legacy) vs sync (headless) script dispatch semantics differ

Becomes the foundation for issue #677 (multi-menubar enumeration), the
detailed Phase 6 plan (task #20, blocked on this), and editor-window
work whenever that arrives.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jsavin added a commit that referenced this pull request Jun 1, 2026
…acy parity) (#688)

* feat(menu): Phase E - wire menu content via windowTypes manifest (legacy parity)

Final phase of the menu-port plan. Achieves functional parity with the
legacy Frontier GUI menu system in the headless REPL.

What ships:
- New "frontier" menubar with File / Edit / View menus, installed at
  REPL boot via the Phase C bridge -> windowTypes framework -> new
  ReplWindow.openWindow handler
- File menu: New, Open, Close, Save, Save As, Quit
- Edit menu: Find, Find Next, Replace, Replace and Find Next,
  Insert Date/Time
- View menu: Huge, Medium, Tiny, Readable
- Adapters under system.menus.handlers.repl that bridge the kernel-fired
  system.callbacks.openWindow("<path>") to the windowTypes framework
  (strips @ prefix that address() rejects)
- File>Quit and REPL>Exit dispatch-target-identical (both call
  repl.exit) - resolves task #21

Palette strip at boot: "Edit  File  View  REPL" (alphabetical union of
the two installed bars, matching Phase A's enumeration contract).

Design decisions:
- Bar named "frontier" sorts before "repl" so File/Edit/View lead the
  strip per legacy convention
- File>Quit wired directly to repl.exit (not commands.quit which walks
  GUI windows that don't exist in headless)
- Edit Cut/Copy/Paste deferred - no terminal analog in classic Frontier
- Adapter scripts (not inline lambdas) because UserTalk does not support
  taking the address of a script inside a bundle block
- Adapter strips leading @ from WINDOW_BRIDGE_REPL_PATH because address()
  rejects strings with a leading @

Tests:
- 6 new behavioral integration tests in replwindow_openwindow_menus_phase_e.yaml
- All 9 Phase D regression tests continue to pass
- Unit: 492/492; Integration: +6 new tests, all green; pre-existing
  html/tcp baseline unchanged

Virgin.root: regenerated + compacted (10.5 MB, +9.5 KB net for 3 new
scripts + 1 modified). Live-tested by JES before commit per task #15.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(menu): address PR #688 round-1 review feedback

Three items from /gate round-1:

bar-raiser P1-1: Tighten File>Quit script-payload assertion (test #6)
  Was `contains "repl.exit"` -- matches repl.exitNow, repl.exit2, etc.
  Now `contains "repl.exit ("` to lock down the exact dispatch contract.

bar-raiser P1-2: Adapter parameter mutation -> local copy
  windowTypesOpenAdapter and windowTypesCloseAdapter were mutating the
  formal parameter `name` in place. UserTalk parameter-passing semantics
  for shared buffers are not formally guaranteed; defensive idiom is to
  copy to a local. Matches the convention used elsewhere in
  Frontier.tools.windowTypes.*

security P1: closeWindow adapter discards framework veto
  Was unconditional `return (true)`. Framework's closeWindow returns
  false when a save-confirmation dialog is cancelled -- the kernel-level
  close chain interprets that as "abort the close". Phase E (REPL only)
  has no save dialog so it's moot today, but Phase F editor windows
  with unsaved-change prompts would silently close + lose user edits
  with the old code. One-line correctness fix:
    return (Frontier.tools.windowTypes.callbacks.closeWindow (adr))

Virgin.root regenerated to pick up the new adapter bodies, then
re-compacted: 10.5 MB -> 13.4 MB (install churn) -> 11.0 MB (compact).

Tests: 492/492 unit, 15/15 Phase D+E. P2 items deferred to issue #686
(Phase F hardening).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
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.

1 participant