Skip to content

chore(OMN-15989): make .onex_state disposable-by-default with two named durable subtrees - #1609

Merged
github-actions[bot] merged 1 commit into
devfrom
jonah/omn-15989-onex-state-disposable-rule
Aug 28, 2026
Merged

github-actions[bot] merged 1 commit into
devfrom
jonah/omn-15989-onex-state-disposable-rule

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Aug 28, 2026 •

Copy link
Copy Markdown
Contributor

OMN-15989 — .onex_state/ is disposable by default, with two named durable subtrees

This is the second of two surfaces for OMN-15989. The first, omniclaude#2061, merged 2026-08-28T19:45:16Z and taught the canonical teardown tool (scripts/prune-worktrees.sh) the same rule. This PR writes the rule into the repo where the failure was actually observed, so it applies at git status time rather than only at teardown time.

Premise check first — the ticket's example was stale, its mechanism was not

The ticket cites .onex_state/consumer-graph.json. That file was already gitignored here by OMN-15431 (merge d4831d76, 2026-08-01) — twelve days before the ticket was filed — and the ledger line it cites (docs/tracking/2026-07-09-codex-merge-controller-ledger.md:4776) does not exist; that file is 2258 lines. Recorded on the ticket before any code was written.

The failure class is live. A bounded scan (1037 worktrees enumerated via git worktree list --porcelain across every canonical clone, narrowed to those holding a .onex_state/ dir, then git status --porcelain --untracked-files=all on each) finds six worktrees dirty solely from untracked .onex_state scratch, with zero non-.onex_state dirt — all of them in this repo:

worktree untracked .onex_state entries
OMN-16177/omnibase_core push_exit_code.txt, push_log.txt
OMN-16346/omnibase_core MOVED_TO_201.md, omn16346-prepush-waiter.sh, .out
OMN-16507/omnibase_core MOVED_TO_201.md, omn16507-prepush-waiter.sh, .out
OMN-16589/omnibase_core MOVED_TO_201.md
OMN-16625/omnibase_core MOVED_TO_201.md
OMN-16677/omnibase_core OMN-16677-push-handoff.md

Each one turns teardown into a per-incident owner-approval decision about a file a command can rewrite. Not one of them is real work.

The rule (AC1)

Inside a repo checkout, .onex_state/ is disposable. The only durable content is the two named subtrees .onex_state/evidence/ and .onex_state/friction/. State that must outlive the checkout lives outside every worktree at $OMNI_HOME/.onex_state/ — lane scratch, ledger locks, claim/run state (cf. omnibase_infra/scripts/lane_scratch.py, whose root resolves there for exactly this reason), so teardown cannot take it.

The rule ships as prose next to the patterns, and a test asserts it stays there — a bare pattern block with the rationale stripped is how the separation decays back into per-file judgement.

Both sides of the line, named (AC2)

The durable side is not a guess: git ls-tree -r --name-only origin/dev -- .onex_state returns exactly 7 paths, and every one is under evidence/ or friction/.

Durable — stays visible to git status: .onex_state/evidence/** (6 tracked files) and .onex_state/friction/** (1 tracked file).

Disposable — ignored: everything else at .onex_state/*. Observed instances: consumer-graph.json / .lock / .json.*.tmp (OMN-15431), push_log.txt, push_exit_code.txt, MOVED_TO_201.md, *-prepush-waiter.sh / .out, OMN-16677-push-handoff.md, plus nested per-run output (local_runtime/, tmp/, lane_scratch/).

The three consumer-graph lines are replaced, not dropped — .onex_state/* is a strict superset of them. Their OMN-15431 rationale is folded into the new block.

Shape: .onex_state/* then !.onex_state/evidence/ + !.onex_state/friction/. The /* form is load-bearing and commented in place: ignoring .onex_state/ outright excludes the directory itself, and git cannot re-include a path whose parent directory is excluded, which would make both negations dead.

TDD, red first

tests/validation/test_onex_state_disposable_gitignore.py drives real git check-ignore --no-index against this repo's real .gitignore — --no-index because check-ignore otherwise refuses to report a tracked path as ignored, which would make the durable-side assertions pass for the wrong reason. The env is scrubbed via scrub_git_location_env (OMN-14891) so a hook-context GIT_DIR cannot silently retarget another repo.

RED (.gitignore reverted to origin/dev, final test file): 8 failed, 12 passed. Every failure is a live artifact from the table above, plus the written-down-rule assertion. The 12 passes are correct: 3 consumer-graph paths were already covered by OMN-15431, and the durable + negative-control cases must pass on both sides of the fix.

GREEN (test_onex_state_disposable_gitignore.py + both pre-existing test_gitignore_baseline*.py): 41 passed — 20 new, 21 pre-existing baseline tests unchanged.

Live end-to-end proof (AC3 / AC4 / AC5)

Three throwaway detached worktrees off this repo, identical disposable artifacts in each, using the real names observed above:

worktree .gitignore contents git status --porcelain git worktree remove (no --force)
before origin/dev disposable only 3 lines rc=128 contains modified or untracked files — still on disk
after this branch disposable only 0 lines rc=0 — removed
negative this branch disposable + a real edit to src/omnibase_core/__init__.py 1 line ( M src/omnibase_core/__init__.py) rc=128 — still on disk

before and after differ only in the .gitignore, which makes the rule the demonstrated cause rather than a correlate. The negative row is the guard the ticket asks for: this must never widen into a mask over unpushed work. A parametrised negative control in the test file covers the same ground statically over src/, tests/, docs/, and .gitignore itself.

Shared location, not per-machine (AC6)

The rule lives in the tracked .gitignore and in the tracked scripts/prune-worktrees.sh — never in .git/info/exclude or a core.excludesFile, so it travels with the clone and cannot diverge per machine by construction.

One correction to the ticket's wording: it asks to "verify on both .200 and this Mac." This Mac is .200 (ipconfig getifaddr en0 → 192.168.86.200, hostname Stickybeatz-Studio.local), so that is one host, not two. Being tracked repo content, the rule is machine-independent by construction rather than by two-host testing; the honest statement is that the two-host check is not available to be run, not that it passed.

Known limit, stated rather than papered over

A .gitignore cannot retroactively clean a worktree that already exists — a worktree checks out its own branch's .gitignore, so this rule is absent from the tree of any worktree branched before it merges. The six worktrees above are unblocked by the already-merged omniclaude#2061 teardown-tool half, not by this PR. This PR closes the class going forward. That division is why the ticket's AC6 permits either surface and this ticket landed both.

Local verification

ruff format (unchanged), ruff check (all passed), mypy (no issues) on the new test. Governed pre-push selector ran on push — no override flag, no --no-verify, no bypass.

Ticket: OMN-15989

Evidence-Ticket: OMN-15989
Evidence-Source: OCC#7469

…ed durable subtrees

The teardown-blocking class this closes: an untracked, regenerable artifact
under a worktree's own .onex_state/ leaves a landed, 0-ahead/0-behind worktree
dirty, and a dirty worktree escalates removal into a per-incident
owner-approval gate. Six omnibase_core worktrees are blocked that way right
now (OMN-16177/16346/16507/16589/16625/16677), none of them by real work.

The rule replacing the per-file judgement call: inside a repo checkout,
.onex_state/ is DISPOSABLE; the only durable content is the two named subtrees
.onex_state/evidence/ and .onex_state/friction/, which stay visible to
git status. State that must outlive the checkout lives outside every worktree
at $OMNI_HOME/.onex_state/ (lane scratch, ledger locks, claim/run state),
which nothing here can reach.

The pattern is .onex_state/* plus two negations, not .onex_state/ -- git
cannot re-include a path whose parent directory is excluded, so the directory
form would make the negations dead. The three explicit consumer-graph lines
from OMN-15431 are subsumed by the top-level rule; their rationale is kept in
the comment.

Test names the line on both sides and carries a negative control asserting
src/, tests/, docs/ and .gitignore itself are never swallowed. Its git call is
env-scrubbed per OMN-14891.
@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 49 minutes.

View limit details

Limit details: You’ve used the included review currently available. Your 132 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f1c1f097-e886-4ec4-afef-e882b8c753e4

📥 Commits

Reviewing files that changed from the base of the PR and between 8c41a3c and 1e62885.

📒 Files selected for processing (2)
  • .gitignore
  • tests/validation/test_onex_state_disposable_gitignore.py

Comment @coderabbitai help to get the list of available commands.

@github-actions
github-actions Bot enabled auto-merge (squash) August 28, 2026 21:48
jonahgabriel pushed a commit to OmniNode-ai/onex_change_control that referenced this pull request Aug 28, 2026
…#7469)

* evidence: OCC companion pass 1 for OmniNode-ai/omnibase_core#1609

* evidence: OCC companion self-bind for #7469

---------

Co-authored-by: node-occ-companion-effect <occ-companion-effect@omninode.ai>
@github-actions
github-actions Bot merged commit f8eef25 into dev Aug 28, 2026
181 checks passed
@github-actions
github-actions Bot deleted the jonah/omn-15989-onex-state-disposable-rule branch August 28, 2026 22:41
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