Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions project/TICKETS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ analysis artifacts.
| **ticket-005** | [`README.md`](./ticket-005/README.md) | [`preprompt.md`](./ticket-005/preprompt.md) | - | [`ai-codex.md`](./ticket-005/ai-codex.md) | [`ai-codex-logs.txt`](./ticket-005/ai-codex-logs.txt) | [`changelog.md`](./ticket-005/changelog.md) |
| **ticket-006** | [`README.md`](./ticket-006/README.md) | [`preprompt.md`](./ticket-006/preprompt.md) | - | [`ai-codex.md`](./ticket-006/ai-codex.md) | [`ai-codex-logs.txt`](./ticket-006/ai-codex-logs.txt) | [`changelog.md`](./ticket-006/changelog.md) |
| **ticket-007** | [`README.md`](./ticket-007/README.md) | [`preprompt.md`](./ticket-007/preprompt.md) | - | [`ai-codex.md`](./ticket-007/ai-codex.md) | [`ai-codex-logs.txt`](./ticket-007/ai-codex-logs.txt) | [`changelog.md`](./ticket-007/changelog.md) |
| **ticket-008** | [`README.md`](./ticket-008/README.md) | [`preprompt.md`](./ticket-008/preprompt.md) | - | [`ai-codex.md`](./ticket-008/ai-codex.md) | [`ai-codex-logs.txt`](./ticket-008/ai-codex-logs.txt) | [`changelog.md`](./ticket-008/changelog.md) |
| **ticket-009** | [`README.md`](./ticket-009/README.md) | [`preprompt.md`](./ticket-009/preprompt.md) | - | [`ai-codex.md`](./ticket-009/ai-codex.md) | [`ai-codex-logs.txt`](./ticket-009/ai-codex-logs.txt) | [`changelog.md`](./ticket-009/changelog.md) |
<!-- AUTO:TICKET_INDEX:END -->
28 changes: 28 additions & 0 deletions project/ticket-008/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Ticket 008: Reduce Process URI estimation overhead

- **ID**: ticket-008
- **Owner**: unresolved:human
- **Status**: IN_PROGRESS
- **Workflow state**: PUBLICATION
- **Created**: 2026-08-25

## Goal and scope

Reduce the observer effect of Process URI estimation while preserving bounded,
append-only evidence. Use a one-second default sample interval and read only
the tail of the event log when extending its hash chain.

## Acceptance criteria

- [x] AC-01: Scope is approved by the user's request to measure and reduce
Subactor resource consumption.
- [x] AC-02: Command and PID observation default to one sample per second.
- [x] AC-03: Appending an event does not scan the full historical JSONL file.
- [x] AC-04: Tests prove the new defaults and bounded tail read.
- [x] AC-05: Pytest and governance checks pass.

## Participants

- Human participant: user:tom; authorization is recorded by the originating
request and no synthesized user file was created.
- Agent participant: [ai-codex.md](ai-codex.md)
8 changes: 8 additions & 0 deletions project/ticket-008/ai-codex-logs.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
2026-08-28 validation

- `PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src python -m pytest -q`: PASS
(10 passed).
- `PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src ./project/governance-check.sh`:
PASS (0 errors, 0 warnings).
- The four implementation/test blobs match the original PR #1 head exactly.
- Plan commit `a67e364` precedes implementation commit `391d5af`.
39 changes: 39 additions & 0 deletions project/ticket-008/ai-codex.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
participant-id: agent:codex
participant: codex
role: agent
ticket: ticket-008
---
# Participant: codex (AI agent)

## Understanding

The 10 Hz default consumed about 1.62 CPU seconds during a six-second sample;
the same workload at 1 Hz consumed about 0.36 CPU seconds, a reduction near
78 percent. Event append also rereads all prior evidence and grows linearly.

## Execution plan

1. Change observer defaults from 10 Hz to 1 Hz.
2. Resolve the prior event from a bounded tail read.
3. Add regression tests for both resource guardrails.
4. Run pytest and governance, then publish through validator-agent.

## Actual changes

- Initialized the bounded ticket and recorded SESSION_EXECUTION_AUTHORIZATION
from the request to execute this work.
- Changed command and PID sampling defaults from 10 Hz to 1 Hz.
- Replaced the linear prior-event scan with a positional read capped at the
final 4096 bytes while preserving append-only hash-chain semantics.
- Preserved the original implementation blobs exactly while rebuilding the
delivery as separate plan, implementation and evidence commits.
- Passed ten functional tests and the complete governance check locally.

## Blockers

- None inside the recorded intent; proceed without a second confirmation.
- New authority remains required for destructive action, secret access, new
external coordination or material objective expansion. Protected delivery
may be invoked without another prompt when publication is in scope; its
exact-head trusted approval remains independent evidence.
10 changes: 10 additions & 0 deletions project/ticket-008/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Ticket Changelog (ticket-008)

- Reduce command and PID observation defaults from 10 Hz to 1 Hz.
- Resolve the prior JSONL event with a bounded final-chunk positional read.
- Add regression coverage for the sampling and append-cost guardrails.

## [0.1.0] - 2026-08-25

- Initial governance scaffold created.
- No human participant identity or content was generated.
71 changes: 71 additions & 0 deletions project/ticket-008/intent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"schema": "new-project.intent/v3",
"ticket": "ticket-008",
"summary": "Reduce Process URI estimation overhead",
"workstream": "application",
"classification": {
"kind": "SERVICE",
"priority": "P2",
"origin": "health"
},
"allowedPaths": ["project/ticket-008/**", "TODO.md", "project/TICKETS.md", "src/**", "tests/**"],
"delivery": {
"acceptedBaseSha": "4ff7e4d6e288697bac10e2008524f65c0e8280de",
"targetBranch": "main",
"outcome": "Process URI estimation samples at 1 Hz by default and extends its event hash chain from a bounded tail read.",
"nonGoals": [
"Change Process URI authorization or scheduling authority.",
"Change the persisted sample or event schema."
],
"complexity": "S",
"estimatedMinutes": 25,
"budgets": {
"maxImplementationFiles": 5,
"maxAffectedComponents": 1,
"maxPublicInterfaceChanges": 0,
"maxRuntimeDependencies": 0
},
"architecture": {
"status": "accepted",
"decision": "Keep estimation advisory and append-only; reduce sampling frequency and resolve only the final JSONL record with positional reads.",
"components": [
{
"name": "estimation-runtime",
"paths": ["src/**", "tests/**"]
}
],
"responsibilityChanges": false,
"interfaceChanges": [],
"dataChanges": [],
"ui": {
"impact": "none",
"states": [],
"evidence": []
},
"rollback": "Restore the 0.1 second defaults and sequential last-event scan; stored JSONL remains compatible."
},
"runtimeDependencies": [],
"validation": [
{
"criterion": "AC-02",
"commands": ["PYTHONPATH=src python -m pytest"],
"evidence": "Signature regression test asserts one-second defaults."
},
{
"criterion": "AC-03",
"commands": ["PYTHONPATH=src python -m pytest"],
"evidence": "Tail-read regression test limits the final append to one 4096-byte pread."
},
{
"criterion": "AC-05",
"commands": ["PYTHONPATH=src ./project/governance-check.sh"],
"evidence": "Governance report must return GOV-PASS."
}
]
},
"forbiddenPaths": ["project/ticket-*/user-*.md"],
"stacks": [],
"dependsOn": [],
"conflictsWith": [],
"integrationTicket": null
}
15 changes: 15 additions & 0 deletions project/ticket-008/preprompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Ticket preprompt

- **Task ID**: ticket-008
- **Task title**: Reduce Process URI estimation overhead
- **Created**: 2026-08-25T17:11:14Z

Keep executable implementation outside this governance/evidence directory.
Read a human-owned user-*.md file only when one exists.
The request to execute this work creates SESSION_EXECUTION_AUTHORIZATION;
proceed within the recorded intent without a redundant confirmation prompt.
Require new authority for destructive action, secrets, external coordination,
or material objective expansion. When publication is in scope, session
authorization permits the declared protected delivery process and its merge
after exact-head trusted approval without another prompt. Session prose is
never approval evidence and the agent must not merge directly.
2 changes: 1 addition & 1 deletion src/estimation/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def _context(parser: argparse.ArgumentParser) -> None:
parser.add_argument("--correlation-id", default=os.getenv("SUBACTOR_CORRELATION_ID"))
parser.add_argument("--store", default=DEFAULT_STORE)
parser.add_argument("--events", default=DEFAULT_EVENTS)
parser.add_argument("--interval", type=float, default=0.1)
parser.add_argument("--interval", type=float, default=1.0)


def _parser() -> argparse.ArgumentParser:
Expand Down
4 changes: 2 additions & 2 deletions src/estimation/monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def measure_command(
process_key: str | None = None,
ticket_id: str | None = None,
correlation_id: str | None = None,
interval_seconds: float = 0.1,
interval_seconds: float = 1.0,
) -> Sample:
argv = [str(item) for item in command]
if not argv:
Expand Down Expand Up @@ -157,7 +157,7 @@ def observe_pid(
process_key: str | None = None,
ticket_id: str | None = None,
correlation_id: str | None = None,
interval_seconds: float = 0.1,
interval_seconds: float = 1.0,
duration_seconds: float = 10.0,
) -> Sample:
root = psutil.Process(int(pid))
Expand Down
22 changes: 15 additions & 7 deletions src/estimation/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from contextlib import contextmanager
import hashlib
import json
import os
from pathlib import Path
from typing import Any, Iterator

Expand Down Expand Up @@ -42,14 +43,21 @@ def _append_line(path: Path, payload: dict[str, Any]) -> None:


def _last_event(handle: Any) -> tuple[int, str]:
handle.seek(0)
last: dict[str, Any] | None = None
for raw_line in handle:
line = raw_line.strip()
if line:
last = json.loads(line)
if last is None:
handle.flush()
offset = os.fstat(handle.fileno()).st_size
suffix = b""
while offset > 0:
size = min(4096, offset)
offset -= size
suffix = os.pread(handle.fileno(), size, offset) + suffix
stripped = suffix.rstrip()
separator = max(stripped.rfind(b"\n"), stripped.rfind(b"\r"))
if separator >= 0:
last = json.loads(stripped[separator + 1 :].decode("utf-8"))
return int(last["sequence"]), str(last["eventHash"])
if not suffix.strip():
return 0, ZERO_HASH
last = json.loads(suffix.strip().decode("utf-8"))
return int(last["sequence"]), str(last["eventHash"])


Expand Down
27 changes: 27 additions & 0 deletions tests/test_estimation.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import inspect
import json
import os
from pathlib import Path
Expand All @@ -10,6 +11,7 @@
from estimation.monitor import measure_command, observe_pid
from estimation.stats import aggregate_samples, estimate_workload
from estimation.store import append_sample, load_samples
import estimation.store as store_module


def _sample(duration: float, memory: int, *, outcome: str = "succeeded"):
Expand Down Expand Up @@ -72,6 +74,31 @@ def test_event_store_builds_a_hash_chain(tmp_path: Path) -> None:
assert all(row["secretMaterialIncluded"] is False for row in rows)


def test_monitor_defaults_to_one_hertz() -> None:
assert inspect.signature(measure_command).parameters["interval_seconds"].default == 1.0
assert inspect.signature(observe_pid).parameters["interval_seconds"].default == 1.0


def test_event_append_reads_only_bounded_tail(tmp_path: Path, monkeypatch) -> None:
store = tmp_path / "samples.jsonl"
events = tmp_path / "events.jsonl"
for index in range(1000):
append_sample(_sample(float(index + 1), index + 1), store, events)

original_pread = store_module.os.pread
bytes_read = 0

def measured_pread(fd: int, size: int, offset: int) -> bytes:
nonlocal bytes_read
chunk = original_pread(fd, size, offset)
bytes_read += len(chunk)
return chunk

monkeypatch.setattr(store_module.os, "pread", measured_pread)
append_sample(_sample(1001.0, 1001), store, events)
assert bytes_read <= 4096


def test_report_and_workload_estimate_use_successful_p90() -> None:
samples = [_sample(1.0, 100), _sample(2.0, 200), _sample(3.0, 300), _sample(20.0, 999, outcome="failed")]
report = aggregate_samples(samples)
Expand Down
Loading