Skip to content

Fix/little improvements#107

Merged
donk8r merged 3 commits into
masterfrom
fix/little-improvements
Jul 13, 2026
Merged

Fix/little improvements#107
donk8r merged 3 commits into
masterfrom
fix/little-improvements

Conversation

@donk8r

@donk8r donk8r commented Jul 13, 2026

Copy link
Copy Markdown
Member

No description provided.

donk8r added 3 commits July 13, 2026 16:30
- Implement working-tree fingerprinting using git or file metadata
- Track workdir fingerprints to detect mutations between rounds
- Add is_verifier_shaped to identify verification tools
- Implement round-based verification tracking and state management
- Extract and recite negative imperatives/constraints from tasks
- Integrate fingerprinting and constraints into response processing
- Add workdir module and expand test suite for constraint handling
- Add input tokens, output tokens, and cost to CompressionStats
- Record AI decision costs regardless of compression outcome
- Display detailed compression spend in /info command
- Update /info logic to show stats if tokens were consumed
- Move inline JSON requests to dedicated helper functions
- Add schema validation tests for outgoing ACP parameters
- Fix protocolVersion type from string to integer in smoke tests
@github-actions

Copy link
Copy Markdown

Octomind — developer:brief (octohub:glm)

Now I have all the context needed. Let me produce the brief.

📦 Brief: Observational verification via git fingerprints, constraint recitation, compression spend tracking, and ACP protocol version fix

Overall risk: 🟡 MEDIUM · Cards: 4

# Change Risk Confidence
1 Replaces tool-name-based mutation tracking with git working-tree fingerprinting to detect unverified changes 🟡 ●●●
2 Extracts and recites explicit prohibitions from user request into every API call's context tail 🟢 ●●●
3 Tracks compression decision model's own token/cost spend in /info 🟢 ●●●
4 Fixes ACP protocolVersion from string "0.1.0" to integer 1, extracts params to schema-validated functions 🟢 ●●●

Card 1/4: Observational verification replaces tool-name-based mutation tracking · 🟡 · ●●●

INTENT
The commit edb9a650 says "implement observational verification" — the prior approach classified tools by name to guess whether a mutation was made and whether a later call verified it. This replaces that guesswork with git working-tree fingerprinting: measure the tree before and after each tool round, and a round "verifies" only when a verifier-shaped call succeeded on an unchanged tree.

WHAT CHANGED

  • Detectors.unverified_mutation: bool replaced with verified_fp: Option<u64> (last clean-verification fingerprint) + dirty_shape: bool (fallback when fingerprints unavailable)
  • New supervisor::workdir::fingerprint() spawns git status --porcelain -uall, hashes the output + (size, mtime) of every dirty path → Option<u64> (None outside a git repo)
  • needs_verification() now takes a live fingerprint argument and compares against verified_fp; falls back to dirty_shape when fingerprints are None
  • note_call() no longer updates verification state — verification moved to new note_round_verification() called once per tool round (not per call)
  • New is_verifier_shaped(): a call is a verifier candidate if it has a string command parameter AND the tool is not from a builtin control-plane server (core/runtime/orchestration/agent)
  • response.rs now fingerprints the tree before and after each tool round, tracks round_verifier/round_mutation flags, and calls note_round_verification()
  • api_executor.rs calls needs_verification(fingerprint()) at pre-gate decision time — a second git spawn per turn

IMPACT RADIUS

  • api_executor.rs pre-gate (line 343): the mutation-verification check now spawns git twice per turn when require_check_after_mutation is enabled — once in response.rs per round, once here at decision time. On a non-git repo, both return None and the system degrades to dirty_shape (the old behavior, roughly).
  • messages.rs reset_streak() comment updated — verification state intentionally survives task resets, same as before but now tracking fingerprints instead of a boolean.

RISK
🟡 MEDIUM. The fingerprint function uses std::process::Command::new("git") — a blocking synchronous subprocess call. It's invoked from response.rs (async context, once per tool round) and from api_executor.rs (once per pre-gate check). In a session with many tool rounds, this is 2+ blocking git spawns per turn on the async runtime. The codebase pattern says "CPU work belongs in spawn_blocking" — this is I/O work (subprocess) that blocks the tokio worker thread. On a fast SSD with a small repo the cost is negligible, but on a large repo or slow filesystem this can stall the async runtime. The DefaultHasher used is not cryptographically stable across platforms/versions, but since it's only compared within a single session, this is acceptable.

DIVERGENCE
🧩 Incomplete changeis_verifier_shaped calls get_tool_server_name() which reads the global TOOL_MAP. In unit tests the tool map is empty, so the None => true branch is always taken (the test comment acknowledges this). The control-plane exclusion is only exercised in integration. This is a known gap, not a bug, but the exclusion logic is untested.

📎 Source

Card 2/4: Constraint extraction and recitation into every API call · 🟢 · ●●●

INTENT
Models violate explicit prohibitions ("do not modify tests") as prompt attention decays over long sessions. This extracts those prohibitions from the user's original request and re-recites them verbatim at the context tail every turn, alongside the existing goal/plan recitation.

WHAT CHANGED

  • New recite::extract_constraints(task: &str) -> Vec<String> — scans lines for negation markers ("do not ", "don't ", "never ", "must not ", "not allowed", "forbidden"), deduplicates, caps at 8 entries, filters out questions and lines >200 chars
  • recite_note() signature gains constraints: &[String] parameter; constraints rendered as a bullet list under "Constraints from the request — verbatim, still binding"
  • api_executor.rs calls latest_real_user_task_content()extract_constraints() → passes to recite_note() every turn
  • gate.rs prompt updated to instruct the verifier to check for violated prohibitions as gaps

IMPACT RADIUS

  • recite_note() is called from api_executor.rs (line 194) on every API call when supervisor+recite are enabled. All existing callers updated with the new &[] argument.
  • The gate prompt change (gate.rs) affects the LLM verify-gate's system prompt — it now explicitly asks the verifier to check prohibitions against ground truth.

RISK
🟢 LOW. The extraction is high-precision by construction (short non-question sentences with negation markers) and capped at 8 entries. The recitation is wrapped in <pay-attention> which the gate already excludes from real-task search. No behavioral break — recite_note with empty constraints behaves identically to before.

📎 Source

Card 3/4: Track compression decision model's own token/cost spend · 🟢 · ●●●

INTENT
The compression decision model is a separate model from the agent. Its spend was previously invisible in /info — only the aggregate total_cost reflected it. This adds per-component tracking so /info can break out the compression model's own input/output tokens and cost.

WHAT CHANGED

  • CompressionStats gains three #[serde(default)] fields: input_tokens: u64, output_tokens: u64, cost: f64
  • conversation_compression/ai.rs records usage from every compression decision call — even when the decision is "don't compress" and even when ignore_cost is true (which only controls the session total, not this per-component tracking)
  • /info command shows compression stats when input_tokens > 0 even if no compressions occurred
  • display.rs renders the new token/cost breakdown in the compression section

IMPACT RADIUS

  • CompressionStats is serialized as part of SessionInfo — the #[serde(default)] ensures backward compatibility with existing saved sessions that lack the new fields.
  • info.rs condition for showing compression stats widened: total_compressions() > 0 || input_tokens > 0.

RISK
🟢 LOW. New fields are #[serde(default)] so old sessions deserialize fine. The spend recording is additive — it doesn't change the total_cost calculation path, only adds a parallel per-component counter.

📎 Source

Card 4/4: ACP protocolVersion string→integer fix + schema-validated param functions · 🟢 · ●●●

INTENT
The agent-client-protocol v1 schema defines protocolVersion as an integer, but Octomind's ACP client was sending "0.1.0" (string). This fixes the wire format and extracts inline JSON params into testable functions pinned against the ACP schema types so a crate upgrade that changes the wire format fails the test instead of breaking at runtime.

WHAT CHANGED

  • protocolVersion changed from "0.1.0" (string) to 1 (integer) in acp_initialize_params(), smoke tests, and integration docs
  • Three inline JSON param blocks in run_acp_command extracted to acp_initialize_params(), acp_new_session_params(), acp_prompt_params()
  • New test outgoing_params_match_acp_schema deserializes each function's output against agent_client_protocol::schema::v1::{InitializeRequest, NewSessionRequest, PromptRequest} — a crate upgrade that changes the wire format breaks the test

IMPACT RADIUS

  • run_acp_command is the ACP client used when the agent calls agent_context_gatherer — the protocol version fix ensures compatibility with ACP servers expecting an integer.
  • Smoke tests (acp.sh, schedule.sh) updated to send protocolVersion: 1.

RISK
🟢 LOW. The schema test is a regression guard — if the ACP crate changes the expected type, the test fails at compile time. The fix aligns the wire format with what the server already expects.

📎 Source

📂 Files changed (16 files, ~449 insertions, ~83 deletions)

{tokens:355100,cost:0.51598}

@donk8r
donk8r merged commit cd5c906 into master Jul 13, 2026
15 checks passed
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