Skip to content

Spend: where the money goes (bank & card lines, bills, cloud costs), checked against the code and the payouts - #72

Merged
New1Direction merged 76 commits into
mainfrom
claude/stoic-lamport-3t9dyl
Oct 29, 2026
Merged

New1Direction merged 76 commits into
mainfrom
claude/stoic-lamport-3t9dyl

Conversation

@New1Direction

Copy link
Copy Markdown
Owner

Stage 2 of reading the business through its own records: accounting and spend. It builds on Operations (#71), uses the same folder, and keeps the same rule of aggregates only.

Spend section

What it reads (monthly aggregates per currency)

Records Kept
Bank, card or ledger lines (any statement CSV with a date, an amount or debit/credit, and a description, payee or category) money in and out, out by category, by known supplier, and deposits from payment processors
Vendor bills billed, open and overdue, by known supplier
AWS cost and usage reports, Google Cloud billing exports, Azure cost exports cost by provider and service
operations.py pull aws Cost Explorer through the user's aws command line (ce:GetCostAndUsage, read-only); writes aggregates only
  • People are never named. A payee is kept only when it names a supplier OntologyEX knows: the supplier map's catalog, plus the forms banks print, like AWS EMEA or GOOGLE *CLOUD. Payroll lines, shops and customers refunded count under their category only. Names too common to tell from a shop or a word (Square, Segment, Neon…) are not matched.
  • Descriptions are only matched, never kept. Neither are AWS resource or account IDs.
  • Mapping: operations.json gives a bank file its currency (most exports have none) and a date format, and flips the sign for card exports that list charges as positive.
  • Withholding: cloud costs describe services, not people, so they are never withheld; bank lines and bills follow the small-count rule.

What it answers that the code can't

  • Suppliers paid against suppliers used, by comparing the bank and bills with the supplier map:
    • paid, not in the code: a tool people use, a service set up elsewhere, or one nobody needs any more;
    • in the code, not paid: a free plan, paid from another account, or not set up for production;
    • payment processors are left out of both, since their fees come out of payouts.
  • Did processor payouts reach the bank? Payouts by the month they landed are compared with processor deposits in the bank lines, over the months both cover except the last. This is rough: monthly, not line by line.
  • Cloud against what comes in: the last complete month's cloud cost as a share of payments taken.
  • Signals in the command center (new Spend category): PAYOUTS_NOT_IN_BANK, DEPOSITS_WITHOUT_PAYOUTS, BILLS_OVERDUE, SPEND_RISING, CLOUD_COST_RISING, NEW_SUPPLIER_PAID, plus the paid-vs-used findings.
  • /operations gains a Spend section:
    • tiles for money in and out, cloud and its share of payments, bills, and payouts reaching the bank;
    • a monthly in/out chart;
    • where money went, and cloud cost by service;
    • the suppliers paid, with whether the code uses each.

Demo and golden

The demo gains:

  • six months of a synthetic business bank account, whose payroll lines name fictional people (tested never to appear);
  • vendor bills;
  • an AWS cost and usage report;
  • one paid payout that never reaches the bank;
  • a web app that declares SendGrid and Sentry.

The page finds:

  • the missing payout, exactly $3,142;
  • the overdue Datadog bill;
  • a spending jump from a one-off legal fee;
  • EC2 costs up 45%;
  • OpenAI paid for the first time;
  • AWS, Datadog, Twilio and OpenAI paid but not in the code;
  • Sentry used in the code but not paid.

The golden fixtures record why the demo's command center, digest and operations ledger changed.

Verification

  • tests/test_operations.py (26 tests; 9 new for spend):
    • no person, payee, description, AWS resource or account leaves the records;
    • suppliers, categories, cloud services and signals;
    • the reconciliation gap equals the missing payout exactly;
    • paid vs used, and its command-center items;
    • debit/credit columns, and a missing currency reported with a hint;
    • card charges listed as positive;
    • Google Cloud and Azure exports;
    • overdue bills;
    • the AWS pull through a fake aws command, including the exact arguments and a failure.
  • python -m unittest discover -s tests: OK. golden.py check --require-rust: 19 OK.
  • Chromium: /operations at 1280px and 390px with no errors and no horizontal overflow.

Generated by Claude Code

claude added 30 commits October 28, 2026 17:02
Port discover_repo.py to Rust with output that matches byte for byte:
the same walk order and budgets, ID quoting, gap ordering, and Python's
json.dumps formatting. The YAML loader is a port of PyYAML's reader,
scanner, and parser plus the SafeLoader resolver, so it accepts and
rejects exactly what PyYAML does (tabs, NEL line breaks, YAML 1.1
scalars such as 1:30).

- discover_repo.py: refuse explicit YAML tags; bound YAML nesting (64)
  and JSON nesting (512) explicitly instead of at Python's recursion
  limit; a TOML float that overflows is an error, not infinity; parse
  failures report a stable category (YAMLError, JSONDecodeError,
  TOMLDecodeError, UnicodeError, DiscoveryError)
- control_room --view-engine rust also discovers with the binary
  (native_view_bridge.native_discover, clean environment, trust checks)
- engine_binary(): $ONTOLOGYEX_ENGINE_DIR, skill bin/, then the
  checkout's release and debug builds
- tests/rust_discover_parity.py: 49 edge-case repositories plus seeded
  manifest mutations; 0 mismatches over 24,000 mutated manifests
- Cargo.lock is now committed for reproducible release builds

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
…oofs

A Rust drop-in for feeds.py: the same ONTOLOGYEX_FEEDS config, the same
observations and status files. HTTP probes and Prometheus targets are
polled in parallel; the OTLP/HTTP receiver (protobuf and JSON, gzip,
bearer token) runs on a small worker pool. No proxy, no redirects,
system trust store.

On a 4-core container: ~1.74M spans/s vs ~98k for feeds.py (17.8x),
8.3 MiB vs 41.5 MiB resident.

feeds.py is tightened where Rust could not match its quirks, refusing
input that was malformed anyway: URLs must be printable RFC 3986 with a
plain host and port; OTLP/JSON and Prometheus bodies are strict UTF-8
JSON with typed fields; truncated gzip is refused; the bearer token is
compared as bytes (a non-ASCII header no longer raises); a non-object
Prometheus response no longer crashes the runner; the status file's
folder is created when no rows were written.

Verification:
- tests/rust_feeds_parity.py: config validation, OTLP decoding (seeded
  byte mutations), Prometheus parsing, span-window replays, store merges
  byte for byte, `once` end to end, and the live receiver
- tests/feeds_conformance.py: classification, retention, and p95 rank
  tables compared across Python, Rust, and Lean
- Lean (OntologyEX.Feeds): down_is_error, threshold_inclusive,
  classify_monotone, keep_at_most, keep_from_input, dropped_are_older,
  p95_index_in_range
- CI runs both parity suites, the feeds conformance table for Rust and
  Lean, and cargo with --locked

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
- release-engine.yml: build the four binaries for Linux (static musl,
  x86_64 and arm64) and macOS (arm64, and x86_64 cross-built); run the
  discovery and feeds parity suites on each native build; on an
  engine-v* tag, publish the archives, SHA256SUMS, and a GitHub build
  provenance attestation. Pull requests build without publishing.
- fetch_engine.py: download a release over HTTPS from github.com only,
  check the archive against SHA256SUMS, and install only the expected
  binaries into the skill's bin/ (no traversal, symlinks, or extra
  files; no silent overwrite). Prints the gh attestation verify
  command. Shipped with the installed skill.
- docs/rust-engine.md: install, run, measured speedups, how parity is
  checked, what is proven, known differences, and how to release.
- README, live feeds, operating-picture reference: point to the engine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
Python 3.11.9's urlsplit accepts http://x[::1]/ and http://[::1]x/,
newer versions refuse them; check the shape explicitly so feeds.py and
ontologyex-feeds agree everywhere. The parity harness now prints only
the rows that differ in the end-to-end case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
A manual run of "Engine binaries" with version=engine-vX.Y.Z builds,
runs the parity suites, and publishes the release like a tag push,
creating the tag on the commit it built. The version reaches the shell
through an environment variable and must match engine-vX.Y.Z.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
ontologyex-feeds polls up to 16 targets at once. The fixture used
socketserver's default listen queue of 5, so on macOS the extra
connections were refused outright and the Rust run saw failed probes
where the sequential Python run did not. Linux queues them, which is why
only the macOS job failed (and only sometimes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
…lled

--discovery-engine auto (the default) uses the byte-identical
ontologyex-discover when it is installed (fetch_engine.py or a cargo
build) and Python otherwise; python and rust force a choice. A broken
install still fails loudly instead of silently switching. The server
prints which engine it uses. Graph views stay on Python unless
--view-engine rust, since per-query process overhead outweighs the gain
on small graphs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
feed_setup.py propose reads compose files (and a prometheus.yml a Prometheus
service mounts) and lists candidate feeds, each with the file and line that
declares it: HTTP probes from healthchecks on published ports, OTLP mappings
from OTEL_SERVICE_NAME / service.name, and Prometheus scrape targets that name
compose services. What no declaration backs is a gap, never a guess.
feed_setup.py init writes only the accepted candidates and validates the
config with feeds.load_config.

operate.py up starts the feeds service (Rust when installed) and the control
room on its observations together, and stops both together.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
Both feeds engines now decode trace, span, and parent span IDs. A CLIENT or
PRODUCER span and the SERVER or CONSUMER span whose parent it is (same trace)
count as one call from the first service to the second. Unmatched halves wait
one window, bounded at 100,000 per side. Counts per service pair (at most
1,024) are written to *.calls.json beside the observations, and totals
survive restarts; an unreadable calls file is refused, not overwritten.

The parity suite covers ID decoding, pairing in seeded replays, reading
mutated calls files, merging across emits, and the live receiver's calls file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
dependency_suggest.py reads the calls file the feeds write and proposes
"A depends on B" for each call between mapped components that the
repository does not already declare, with call and error counts and first
and last seen. Calls the repository declares are reported as corroborated;
calls from unmapped services are listed as gaps.

Confirm or reject on the business page (opt-in, token-protected route
/api/dependencies/decision) or with the CLI. A decision is appended
atomically to a dependencies file with who made it and the evidence at
that moment. The control room adds each confirmed dependency as a CALLS
edge with its own evidence record and carries possible impact along it,
like a declared DEPENDS_ON; the systems map draws it in violet.

operate.py passes dependencies.json beside feeds.json and creates an empty
observations file so a traces-only setup starts cleanly. The live demo
now sends real cross-service calls (worker -> payments is suggested), and
the Collector example keeps client spans so calls can be paired.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
decisions.py is the one place link and dependency decisions are written:
confirm, reject, and undo, one or up to 100 at a time, checked whole and
written in one atomic replace (an unknown ID or a file changed meanwhile
refuses the whole batch; the row limit is checked before writing). Undo
removes a decided row so its suggestion comes back; the decided row keeps
the suggestion's ID. Every change is appended to <file>.history.jsonl
with what, who, and when.

- Business page: checkboxes, Select all / Select STRONG, Confirm/Reject N
  selected, Undo for the last action, and a Decided list with Undo per row,
  for both suggested links and observed dependencies.
- Decision routes take "ids" (or one "id") and decision confirm, reject, or
  undo; the business payload lists decided rows (link_decided,
  dependency_decided).
- CLIs: --id repeats, --all-strong, and a new undo command; suggest lists
  decided rows. A batch where every row got the same action reports
  CONFIRMED, REJECTED, or UNDONE as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
otel_setup.py propose lists every app in a repository (Node, Python, Java,
Go, .NET, Ruby, PHP) with the file that declares it, the libraries it will
trace, the name its traces carry (compose service, else package name) and
the map component it stands for. otel_setup.py run --service NAME -- CMD
starts a Node or Python service with OpenTelemetry's zero-code
instrumentation and no code change: Node from a shipped lockfile (npm ci,
189 packages pinned with integrity hashes, install scripts off) loaded with
NODE_OPTIONS; Python from the app's own interpreter (pip --target, the
instrumentations opentelemetry-bootstrap picks) loaded by a sitecustomize
that adds them after the app's packages. Installs go to a cache once.
SIGTERM still stops both: Node's register hook swallowed it, Python's
default skipped the last spans. feed_setup.py offers the name mappings.

The feeds receivers (feeds.py and ontologyex-feeds) refused bodies without
Content-Length, which is how OpenTelemetry JS sends them (chunked), so Node
services could not report at all. Both now read chunked bodies with the
same rules and the same 4 MB cap; the parity suite covers valid, extended,
oversized and malformed chunked requests.

CI runs a real end-to-end: a Node service calls a Flask service, both
started with otel_setup.py run, and the receiver pairs the call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
… to source

For "use OntologyEX to give me an overview of this repository and the
business behind it", an agent now has one path, from AGENTS.md, the README
and SKILL.md: scan, read, fill in a Business X-Ray blueprint, build one page.

- overview.py scan reads a repository the way a new engineer would and
  writes scan.json and a blueprint draft: apps with languages and
  frameworks, compose services and datastores, outside services by area
  (payments, auth, email, AI, data, cloud, analytics), docs, and files to
  read first (entry points, routes, data model, money, users), each with
  its file and line. A rescan keeps an edited blueprint.
- overview.py build refuses leftover placeholders and compiles the page.
- The X-Ray compiler moved into the skill (scripts/business_xray.py;
  business/build_blueprints.py still runs it) and can cite a local checkout:
  every cited line must exist, symlinks out of the repository and paths that
  climb out are refused, and each citation records the quoted lines, their
  SHA-256, and the commit the checkout is on.
- references/business-overview.md is the procedure; AGENTS.md is the first
  page an agent reads; otel_setup finds Rust apps too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
…n the map

Four agents ran the "give this repo to your agent" flow on listmonk, documenso,
saleor and nextjs-subscription-payments. Fixes from what they hit:

- The scan crashed on compose files with YAML anchors; compose is now read with
  anchors and merges allowed, tags refused and expansion bounded.
- A build could succeed while three page tabs were broken: risk severity and
  improvement priority are now checked, money-flow state and the rest defaulted,
  and the reference lists every field with one item of each kind.
- The reading list is ranked by what files import and say, not only their names:
  money and access files, entry points from the Dockerfile or start script, docs
  including AGENTS.md and ADRs, configuration and background jobs.
- Datastores are inferred from drivers, Prisma and Supabase when no compose runs
  them; Go modules, more integrations and frameworks; tooling-only packages marked.
- The page gains a Systems tab and shows each citation's text; the build prints
  each citation's first line to check, and --github links a shared page to GitHub.

code_surface.py reads routes, GraphQL/RPC operations, tables and jobs by syntax
across common frameworks. overview.py lists them; the control room draws them on
a local repository's map under the app that holds them (bounded, API first).

Example page: docs/examples/nextjs-subscription-payments.html, linked from README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
find_apps takes the discovery the scan already made, and both walks build
relative paths from strings instead of pathlib. Same scan.json on saleor,
documenso and listmonk; saleor 5s -> 3.2s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
Two more fresh agents ran the flow on maybe (Rails) and the FastAPI full-stack
template. Fixes from what they hit:

- Code: FastAPI/Flask route lines were two early (a leading \s* ate blank lines)
  and missed router prefixes; SQLModel and pydantic schemas were taken for Django
  tables. Rails routes follow namespaces, nesting, member and collection; ActiveJob
  and Sidekiq jobs and schedule.yml cron entries are found.
- Scan: compose overrides and examples are read (compose.override.yml,
  compose.example.yml); a workspace-only pyproject is tooling; a compose build with
  dockerfile backend/Dockerfile maps to backend/; dev-only packages are not
  integrations; Ruby, banking (Plaid) and more catalog entries; entry points from
  the code that constructs the app, Procfile.*, config.ru; config.py and deploy
  workflows under configuration; .cursor/rules notes as docs; money ranks what the
  business charges above a finance app's own domain words; larger files first.
- Output: scan prints a summary (scan.json has the rest); check_citations shows the
  first and last cited line.
- Page: steps wrap instead of hiding, open questions on the Business tab, plain
  wording, no sideways scroll on phones.
- Docs: drafted systems may be deleted; risk basis guidance; never copy secrets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
…ules

Laravel route groups with prefix(...)->group(...) are followed and routes/api.php is
served under /api; Schema::create migrations are tables; ShouldQueue classes and
Schedule::command/job entries are jobs. The overview reads Laravel entry points
(public/index.php, bootstrap/app.php, artisan), lists app/Models before one-table
migrations, skips lang/ translation files and composer.json in topic ranking, names
a root app after the repository, and knows common Composer packages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
…ecks

Two more fresh agents ran the flow on Basecamp's handbook (documents, no code)
and Monica (Laravel). Fixes from what they hit:

- check_citations read the last line from a quote cut at 600 characters, so long
  prose ranges showed a middle line; it now reads the file. The page keeps up to
  2,000 characters per citation, and the build lists unused citations.
- Documents: money and access ranking reads Markdown too (salary, stipend,
  severance and benefits count as money); docs follow the README's own link order
  and business words rank root documents; the draft cites each document's first
  line; the scan says when a repository is documents, not code; a shallow clone is
  noted. The reference says what systems, stage and journeys mean for documents.
- Laravel: entry points from the Dockerfile's shell scripts, config/<name>.php
  first under configuration, SQLite from .env.example, terms and privacy docs,
  license files out of topic lists.
- Page: wording that fits any business (no "runtime", "blast radius", "business
  anatomy"), improvements name the risks they address, systems grouped by kind.
- Written files take the user's umask instead of 0600.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
Spring routes take a class-level @RequestMapping prefix and path arrays, and
annotations mentioned in comments are skipped; @entity classes are tables (with
@table names when given); @scheduled methods are jobs. The overview reads Maven
and Gradle dependencies (frameworks, integrations, database drivers), finds the
@SpringBootApplication class as the entry point, and lists application*.properties
and .yml under configuration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
claude added 27 commits October 29, 2026 13:51
…code shipped

/digest compares the command-center ranking now with how it stood when the window
began: new, fixed, worse, better, fixed and back again, and still open with how long.
Headline counts and coverage move with a good/bad direction; decisions on links,
dependencies and rules are listed by person; with a local repository, the commits in
the window and the business they touched (git is only read). Copy or download it as
Markdown. The control room records history with --history (operate.py up records to
REPO/.ontologyex/history.jsonl unless --no-history); digest.py record does it from a
bundle. Read-only agent tool weekly_digest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
Declared owners from CODEOWNERS (line cited, last match wins, GitHub/GitLab locations)
and inferred owners from a year of git history (names only, bots left out), mapped to
entities through their own files: service build folders, declaring files, supplier
manifest lines, binding source code, else cited skill files, else the systems a
business item runs on. /owners lists people and teams with what they own, the gaps
nobody owns and code only one person changed; every command-center and digest item
names its owner; entity pages show it; read-only agent tool who_owns. Also fixes a
stray 'null' on entity pages for entities without business rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
…ce in the code

Each pack is a short list of real controls with the evidence a codebase can show for it
(files, CI steps, dependencies, schema fields and migrations, routes, suppliers,
owners), every finding cited by line. Controls are SHOWN, PARTLY_SHOWN or
NOT_SHOWN_IN_SOURCES; what code cannot show is NEEDS_A_PERSON with what the person
provides. Applicability is read from the sources; card-number fields are a risk of
their own. Secret files are never read, key-setting lines are cited without text, and
log lines, seeds and fixtures are not evidence. /compliance with Markdown for the
auditor, ranked gaps in the command center, read-only agent tool compliance_status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
Every page carries a fingerprint of each thing it lets a person decide (link: strength,
signal kinds and the evidence hashes its watermark would record; dependency: strength
and signal kinds, not live call counts; rule: the cited lines' hash; re-confirm: the
evidence now). Decisions send back what they showed; inside the decision lock the
control room recomputes the fingerprints from the same state it records and, if any
changed, writes nothing and answers 409 STALE_VIEW naming what changed. One changed
item refuses the batch. Pages reload and say why. Lean OntologyEX.Stale states the
law. Also a test that every page script parses, after one broke here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
…nst the same files

Parity tests prove the engines agree with each other, not that either is right. Three
inputs (the operating-picture demo, and a shop and a clinic repository under
tests/golden/repos given fixed git histories and read against a fixed clock) and their
outputs (discovery, suppliers, owners, compliance, command center, entity context,
change impact, weekly digest) are stored after normalizing only times and the temp
directory. check prints the JSON paths that differ; CI checks Python, and Rust's
discovery, context and change impact, against the same files. update --reason
records why per file in MANIFEST.json and CHANGES.md; hand edits fail the manifest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
…ly if the repository held still

The code surface, suppliers, git history for owners and compliance evidence ran while a
page waited, under the cache lock. A scanner thread now runs them; pages read the last
committed result and never wait, except the very first request for its first result.
Each scan fingerprints the repository (paths, sizes, mtimes, git HEAD and index) before
and after; a result read while files changed is discarded and re-run. /api/scans
reports each scan. On documenso the slowest page answer after the first fell from 4.2 s
to 0.19 s; golden fixtures unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
A new front page: an animated banner in the control room's own palette, the one-line
agent prompt first, a screenshot grid of six views (synthetic and fixture data only),
a table of all nine views, a readout of real demo output, the MCP tools, how the
model stays honest (standings, watermarks, stale views, scans, nothing executed), the
four layers of verification with their numbers, the Rust engine's measured gains, and
the domain-skill workflow condensed to links into the existing docs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
…closed

tests/bench_rust_engines.py measures discovery, entity context and the OTLP
receiver the way OntologyEX runs them. Per engine: wall time (median, p10-p90
over measured runs after a warm-up), CPU seconds and cores used, peak memory
(sampled from /proc on an untimed run; wait4's ru_maxrss counts the launcher's
memory from before exec). Per workload: whether the answers were the same; a
comparison is computed only when they were. The receiver check counts every
acknowledged span and compares the emitted observation with the Python
reference window. Conditions (machine, versions, build profile, binary
hashes, commit, load) and caveats (debug, noisy, saturated, busy) are
recorded. Missing binaries are NOT_RUN; differing answers exit 1.

The docs quote a saved report through generated blocks (--write-docs), and a
test fails when they disagree. CI runs a quick debug-build pass as a
correctness gate with the report in the job summary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
docs/benchmarks/latest.{json,md} from a release build on a 4-CPU Linux
container: discovery 17.6x faster, the OTLP receiver 34.6x the spans per
second (19.1x per CPU-second), and the same answers on every workload.
Entity context as the control room asks for it is 40x slower on Rust (a
process per answer), which the docs now say instead of hiding.
The README and docs/rust-engine.md tables are generated from the report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
… apart from proofs

What relies on an entity used to stand as whichever chain the traversal reached
first in name order, so renaming two entities could flip a result between
INFERRED and CONFIRMED. The impact page already promised the strongest path.
Python and Rust now keep, per hop count, each entity's strongest chain of
exactly k hops and answer the strongest over 1..4 hops, fewest hops on ties;
depth is that chain's hop count and path is the chain.

Lean: OntologyEXModel/Impact.lean writes the computation; OntologyEXSpec/
Impact.lean states StrongestChain, NothingWithoutAChain, OrderFree and
NameFree; OntologyEXProofs/Impact.lean proves them. The conformance table
gains 300 generated graphs printed from the proven model and compared with
Python (the old code differs on 12). tests/lean_statements.py pins the Model
and Spec files by hash (changes need a recorded reason), forbids proofs in
statements and sorry/axioms in proofs, and in CI holds each theorem to its
exact statement and checks its axioms; leanchecker replays the new modules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
…ndone whole

Every decision is now a compare-and-set on one item: the version of the
decided row the person saw (sha256 prefix of its canonical entry) and what
to write. decisions.commit applies a batch only if every item is still at
that version; otherwise nothing is written (409 STALE_VIEW, naming the
items). A preview returns one digest per operation; applying with those
digests writes only the same batch. Decisions on other items never make an
approval stale. Each applied batch is recorded under a batch ID with every
item's entry before and after; undo_batch applies the inverse, puts back
exactly that batch's items, keeps later decisions on other items, and is
refused when a later decision changed one of its items. Undoing a re-confirm
now restores the previous row; a single-row undo is bound to the version
the page showed. GET /api/{kind}/batches lists batches.

Rust: decide.rs implements the same commit (ontologyex-view decide); the
control room uses it with --view-engine rust after checking at start that
the installed binary has it. tests/rust_decide_parity.py compares it with
Python (3,000 seeded batches in CI); engine/tests/decide.rs checks the laws
on the Rust code and the binary.

Lean: OntologyEXModel/Decisions.lean models commit; OntologyEXSpec/
Decisions.lean states ten laws (stale refused, twice refused, current
applies, whole batch, nothing else, undo restores, undo keeps later
decisions, undo refused after change, independent batches commute, others
never stale); OntologyEXProofs/Decisions.lean proves them. The conformance
table gains 300 generated batches compared with Python.

The page previews bulk confirm/reject and undo, lists recent batches with
Undo batch, and sends row versions with undo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
… aggregates only

operations.py reads payment-processor records dropped in
REPO/.ontologyex/operations (Stripe dashboard exports, Stripe API objects
saved as JSON, other processors through operations.json) and a read-only
Stripe pull, and keeps weekly aggregates per currency: payments, refunds,
disputes, payouts, subscriptions (active and monthly recurring revenue
rebuilt from start, end, price and interval) and invoices. Only the columns
a stream needs are read; customer names, emails, IDs, card details and free
text never are. Amounts and breakdowns of weeks with fewer than three
records are withheld. The pull aggregates each page as it arrives, writes
only aggregates, and refuses secret keys.

Signals compare the last four complete weeks with the four before (open
disputes, failed payouts, rising refunds, falling revenue, high dispute
rate, rising churn, failing payments, written-off invoices) and rank in
the command center under Operations. The records are compared with the
model and code: what happens that the sources do not show, and what is
built that the records never show used.

The control room serves /operations and /api/operations (cached on the
folder's file stats), the pages link to it, agents get operations_status,
and the demo carries 16 weeks of synthetic exports. Golden outputs record
the demo's new command-center items and pin the ledger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
…d costs

spend.py reads business bank, card or ledger lines, vendor bills, and AWS,
Google Cloud and Azure cost exports from the operations folder, as monthly
aggregates per currency: money in and out, out by category, suppliers
paid, deposits from payment processors, bills open and overdue, and cloud
cost by provider and service. A payee is named only when it is a supplier
in the catalog the supplier map uses (matched in payee and description,
which are read for that and nothing else); people paid, shops and
customers refunded count under their category only. operations.py pull aws
runs the aws command line (ce:GetCostAndUsage, read-only) and writes
aggregates only. operations.json now also maps bank files: currency,
date format, and card exports that list charges as positive.

Spend answers what code alone cannot: suppliers paid that the code does not
use and suppliers the code uses that nobody is seen paying; whether
processor payouts reached the bank; cloud cost against payments taken.
Signals (payouts not in the bank, overdue bills, spending and cloud jumps,
a new supplier paid) and the paid-vs-used findings rank in the command
center under Spend; /operations shows a Spend section per currency.

The demo gains six months of a bank account (payroll lines name fictional
people, never kept), vendor bills, an AWS cost report, a missing payout
deposit, and a web app declaring SendGrid and Sentry. Golden outputs record
why the demo's answers changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SoxWvtYbajXo6hFeADTXYF
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.

2 participants