Skip to content

fix(policy): v1.2.43 consolidation — read-lane FP residual + retention landing + one-time prune [t_40022daf] - #65

Open
maximalang wants to merge 17 commits into
codex/company-osfrom
fix/t_40022daf-consolidation
Open

maximalang wants to merge 17 commits into
codex/company-osfrom
fix/t_40022daf-consolidation

Conversation

@maximalang

Copy link
Copy Markdown
Owner

Card

fleet-ops t_40022daf — fleet-policy: context-aware pattern matching (stop false-positive blocks on read-only commands) + enforce retention so the live event-store never regrows unbounded.

Base: codex/company-os @ 9b09444. Version: 1.2.43 (collision guard: {"collision": false, "released_max": "1.2.36"}; open PRs hold 1.2.37–1.2.42).

What this PR is

A consolidation of three in-flight, unmerged fix branches onto current trunk, plus the residual fix that none of them covered:

PR Version Content State in this branch
#56 1.2.37 live lexical-delta capture D1/D2/D2e (t_9412d7d5) merged (pr-56), version-pin conflicts resolved
#57 1.2.38 FP root classes: read-lane CLI, literal-traps as data, truthful remediation, deploy_policy.sh (t_e393b6e8) merged (pr-57) — was CONFLICTING on GitHub, conflicts resolved here
#63 1.2.42 built-in retention, verified archive, size guard, hot-path indexes (t_1d99b96b) merged (pr-63)

The three source cards are done but their PRs never landed, so the live gate kept biting. This PR makes them landable as one unit; the source PRs can be closed after merge.

Residual fix (the gap none of the PRs closed)

Live evidence (policy event-store, ventures .state/fleet-policy.db):

  • event f3b2e0d2cd9e0edaedaccbbaa88849fd7fa1b2703ce03f4ab4222876c09578ba (2026-10-08T14:13:22Z, task t_40022daf call 41): a read-only sqlite3 "file:...state.db?mode=ro" "SELECT ... MATCH '<word>' ...;" 2>&1 | head blocked as irreversible_data_loss / approval_required — the keyword sat inside quoted SQL data.
  • event class of t_3d503c82 (2026-10-08, 06:49/06:58): python -c QA-bench runner whose embedded prompt text tripped the same keyword rule.

Root causes found and fixed:

  1. _SHELL_METACHARACTERS matched the & inside fd duplication (2>&1), contradicting its own comment ("handled by the redirect scanner, not matched here"). Every read pipeline with stderr redirection failed closed into state_change and then into the keyword rules. Fixed: (?<![&>])&(?!&); cmd &, &> f, &>> f still fail closed. (v1.2.23 had pinned the buggy behavior as an adversary case; re-pinned with justification, plus a new adversary proving fd-dup + real file redirect still writes-deny.)
  2. sqlite read lane rejected the standard trailing ; and counted the 2>&1 token as a positional. Both fixed; interior ;, dot-commands, multi-statement chains stay fail-closed.
  3. MUTATOR pre-filter scanned bare search patterns: grep -rn deploy src/ flipped to deploy_external_runtime (evidence-gated at runtime). v1.2.22 masked only quoted spans. Now search-tool operands (grep/rg/findstr/select-string/find patterns, paths, value-flag values) are blanked in the MUTATOR view only — the write-marker scan keeps the raw command, and every stage still passes its own read-only check, so masking cannot launder a mutating stage.
  4. Hardening (direction reversed): the rm keyword anchor accepted only (?:^|\s), so python -c "os.system('rm -rf /x')" and bash -c 'rm -rf ...' slipped through as scoped_state_change/allow. Anchor widened with quote chars; pinned by tests.

Not fixed by design: python -c with prompt text embedded in the code string stays strictly scanned (no nested-literal masking — that would open an os.system evasion). Sanctioned lane: write_file + python file.py (file contents are data). Documented in CHANGELOG.

One-time prune of the live ~450 MB store (D2-b)

  • scripts/prune_policy_db.py — operator-only: pre-flight counts → SQLite backup-API snapshot + MANIFEST.json/sha256 → forced maintenance tick (verified archive before delete, from feat(policy): v1.2.42 built-in event retention, verified archive, size guard, hot-path indexes [t_1d99b96b] #63) → full VACUUM → post-run sidecar re-verify + PRUNE-REPORT.json. Dry-run posture by default; --yes to execute.
  • Runbook: docs/fleet-ops/fleet-policy-prune-runbook.md (preconditions, steps, post-verify, rollback).
  • Worker-side execution against the live store stays denied by the protected-path gate — this runs in the company operator session after deploy.

Verification (local, Windows worktree C:/tmp/fp-t40022daf)

  • uv run --frozen python -m pytest tests/ -q → 1011 passed (merged baseline 981; +25 read-lane battery test_v1243_readlane_fp.py, +3 prune battery test_v1243_prune_script.py, +2 re-pinned v1223 adversaries).
  • uv lock --check OK; compileall src tests scripts integrations OK.
  • fleet-policy --root . drift-check → {"missing": [], "ok": true}; --version → 1.2.43.
  • build-bundle (92 files) + verify-bundle → ok.
  • check_version_uniqueness → {"collision": false, "version": "1.2.43"}.
  • Classification battery before/after on the incident shapes: E1 (sqlite FTS w/ keyword + 2>&1) approval_required → read_only/allow; grep bare/quoted keyword patterns → read_only/allow; TP controls (rm -rf, push -f, reset --hard, drop table, multi-statement splice, backgrounding, &> write redirect, os.system/bash -c evasion) all stay escalated.

Merge protocol

Same as the source PRs: independent QA review on this exact head, company GO anchor, merge by company/operations. After merge: deploy via scripts/deploy_policy.sh (tag route per the deploy runbook), then the one-time prune per the prune runbook.

Finances: 0 ₽ incremental.

maximalang and others added 15 commits October 5, 2026 18:54
…_9412d7d5)

The deployed live clone drifted from trunk again: an uncommitted
classifier patch (source card t_f6acc5dc, archived; the code existed in
no commit or branch, git log --all -S empty) sat in its working tree on
top of trunk policy.py, with tests/test_policy_lexical_delta.py
untracked beside it. Post-merge QA finding F-A (t_85547708) flagged the
repeat of the live-clone-as-sole-carrier pattern from the v1.2.32->34
rescue: any reinstall or checkout to trunk would have lost the fix the
running gate already relies on.

Captured byte-exact onto trunk 2f2f5ee (v1.2.34): policy.py staged blob
cac7780 and test blob
95d7b60 both equal the live canonical
blobs (git hash-object in the live repo). The live clone was NOT
modified - read-only capture; controlled apply follows the merge.

D1 (F1, run2166 call19 class): an EMPTY-value dotted git -c config
    assignment (option reset/disable, e.g. an empty helper value) is a
    config-key NAME, not a filesystem operand; only that form directly
    after git -c is exempt, every non-empty value stays guarded.
D2 (F2, run2172 call26 class): dispatcher worker env-pin bindings
    (HERMES_KANBAN_DB/_BOARD/_TASK/_WORKSPACE/_WORKSPACES_ROOT,
    HERMES_TENANT) as NAME=value tokens bind a child process and mutate
    no store; unknown env names carrying a store path stay denied.
D2e (fail-closed tightening): when the same command expands a bound pin
    ($PIN/${PIN}), the bound value re-enters the path-guard subjects, so
    expansion cannot smuggle a store path past the guard - a shape
    previously allowed is now denied.

Tests: tests/test_policy_lexical_delta.py (captured from the live clone,
13 tests) - FP recurrences F1/F2/F2b plus TP controls proving no
weakening. Full battery on this branch (Windows, uv, CPython 3.12.14,
mirrors CI): pytest tests/ 901 passed plain and under
HERMES_KANBAN_TASK=t_ci_simulated; check_version_uniqueness collision
false (1.2.37, released_max 1.2.34); uv lock --check; compileall;
drift-check ok:true; fleet-policy --version 1.2.37; build-bundle +
verify-bundle green.

Version 1.2.37: 1.2.35/1.2.36 are claimed by open PRs #54/#55; policy.py
may overlap textually with #54 - merge sequencing is the owner's call.
…icy state (events/task/show), sessions/dumps enumeration exemption, read-shaped control-plane deny hint, --root gap closed in self-approval guard [t_e393b6e8]
…are data: task-type inference masking, attestation lines filter (arm/revoke/write-guard/anchors), tightened terminal forgery heuristic, lifecycle channel on broken markers [t_e393b6e8]
…mpany session route via bundle+runbook with [continues: company], tech-card advice scoped to repo sources [t_e393b6e8]
…sed, RR-1 drift gate, JSON manifest + rollback) + fleet-ops runbook [t_e393b6e8]
…sqlite-heredoc CLI-route reason, ls-sessions enumeration matrix, POISON-quote inference, broken-marker lifecycle channel [t_e393b6e8]
…412d7d5 branch): changelog, version pins, lockfile, comment labels [t_e393b6e8]
…lure status, retry throttle [t_7733d471]

F1: PRAGMA auto_vacuum=2 staged in migrate() never persisted —
journal_mode=WAL materializes the db header first (fresh stores were
silently born in mode NONE), and a staged mode does not cross
connections. Issue the pragma BEFORE the WAL switch and re-issue it in
full_vacuum() on the vacuuming connection, so legacy auto_vacuum=0
stores actually convert to INCREMENTAL on the advertised operator full
vacuum. Proven by freelist + file-size reclamation, not a marker.

F2: fleet-policy maintenance exited 0 when the report carried per-batch
errors (fail-closed archive verification). Now exits 1 on error/errors.

F3: a run with errors no longer stamps retention_last_run_epoch, so the
documented retry-next-tick fires; failed attempts are ledgered in
retention_last_failure_epoch / retention_consecutive_failures.

Ports the independent QA assertions (t_218bc548 comment 6212) into
tests/test_v1242_retention_maintenance.py: fresh/legacy conversion +
reclamation proof, mode-NONE negative control, CLI exit assertion,
throttle preservation across failure.
# Conflicts:
#	CHANGELOG.md
#	integrations/hermes/fleet-policy-plugin/plugin.yaml
#	plugin.yaml
#	pyproject.toml
#	src/fleet_policy/__init__.py
#	tests/test_hardening_v121.py
#	uv.lock
# Conflicts:
#	CHANGELOG.md
#	integrations/hermes/fleet-policy-plugin/plugin.yaml
#	plugin.yaml
#	pyproject.toml
#	src/fleet_policy/__init__.py
#	tests/test_hardening_v121.py
#	uv.lock
# Conflicts:
#	CHANGELOG.md
#	integrations/hermes/fleet-policy-plugin/plugin.yaml
#	plugin.yaml
#	pyproject.toml
#	src/fleet_policy/__init__.py
#	tests/test_hardening_v121.py
#	uv.lock
…railing-;, bare search patterns), rm quote-adjacent evasion, one-time prune script [t_40022daf]

- _SHELL_METACHARACTERS: fd duplication 2>&1 no longer trips the bare-& scan
- _sqlite_stage_is_read_only: single trailing ';' + fd-dup tokens tolerated
- MUTATOR view masks search-tool operands (grep/rg/findstr/select-string/find)
- rm keyword anchor accepts quote-adjacent form (closes os.system evasion)
- scripts/prune_policy_db.py: operator one-time prune (backup+manifest ->
  verified archive+delete -> full vacuum -> re-verify+report)
- tests: +25 read-lane battery, +3 prune battery; v1223 fd-dup re-pinned
- consolidates PR #56 (v1.2.37), PR #57 (v1.2.38), PR #63 (v1.2.42)
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