funkalization
funkalization helps a maintainer review design changes: what changed, which obligations remain unresolved, and which evidence still applies. The current experimental release is a local payment–storage tracer with persistent decisions and read-only views.
Reviewed prose and version control already preserve decisions and history. This experiment adds typed changes, revision guards, explicit obligations and scoped machine-readable evidence. Whether those additions reduce review effort is being tested separately from technical correctness.
Try the experiment
Start with Run the design-review tracer, then inspect the CLI reference and workspace storage. The status page separates the working slice from the larger proposed visual workspace.
Design contract
The historical VSD-IMPLEMENTATION-2026-10-13 bundle defines the broader claims C01–C08 and gates G0–G8. The implemented slice is specified by openspec/changes/add-persistent-tracer/; both live in the repository’s openspec directory.
Interactive semantic editing, general composition and safe source write-back are not implemented. A passing model check is scoped evidence, not proof of a running implementation.
Ecosystem
funkalization is an experimental charly-vibes fleet tool. OpenSpec supplies change-management conventions, Specodelic is a prospective integration, wai records research and rationale, and Beads tracks work. Build from source; no published installation is claimed here.
Run the design-review tracer
The tracer demonstrates a storage-substitution review, including a counterexample and stale-decision refusal. Use a Unix machine with Rust, Cargo and a C compiler; run these commands from the repository root.
Build and run
cargo build --locked
cargo run --locked -- tracer target/my-tracer
Both commands exit 0 on success. The tracer prints a JSON report with "passed": true and expected/actual assertions. The directory must not already exist; choose a new name on subsequent runs.
Open target/my-tracer/view.html in your browser. Inspect the bad, good and stale proposals. The bad backend advertises the required operations but allows shipment before persistence. The compatible replacement uses direct payment and permits shipment only within the supplied policy’s allowed traces. Its pass is conditional on that finite corpus and observation map.
Inspect evidence and replay
cargo run --locked -- replay target/my-tracer/workspace.db
cargo run --locked -- view target/my-tracer/workspace.db
Replay exits 0 and prints "replayed": true with the reconstructed state. View exits 0 and prints source-addressed JSON, including all proposal evidence, applicability and export limitations. The HTML file is a read-only snapshot; use view DB NEW.html to generate a refreshed one.
| Artifact | Meaning |
|---|---|
report.json | Expected versus actual tracer assertions and fixture fingerprints |
workspace.db | Persisted decisions, proposals and evidence |
source.json | Original policy and declared initial binding |
export.json | Derived inventory with an explicit loss report |
view.html | Read-only inspection of accepted/proposed bindings and evidence |
Try your own action
Initialize a separate workspace using the checked-in source fixture:
cargo run --locked -- init target/manual.db fixtures/pilot/workspace.json
Expected: exit 0, JSON state with revision 0. Copy one of the action examples from the CLI reference, then run apply target/manual.db ACTION.json. A failed decision exits 2 with a structured error and commits no effect.
Troubleshooting
destination means the output already exists; use a fresh path. source_changed means the original source changed or disappeared; historical replay remains available, but mutations require restoring the exact bytes or starting a new workspace. unresolved means the strict acceptance conditions are unmet; inspect checker results and declared counterexamples. stale means the action targets an earlier accepted revision.
If bundled SQLite compilation fails because a compiler cache points to a read-only directory, give the cache a writable location, for example CCACHE_DIR=/tmp/funkalization-ccache cargo build --locked.
Run just ci for the Rust checks and python3 scripts/reproduce_baselines.py for the recovered Python experiments. These establish different evidence; neither establishes that a production payment service is correct.
Workspace storage and authority
The v1 tracer uses a SQLite file at the path you pass to the CLI. It reads no global configuration and does not automatically discover a .funkalization/ directory.
Persisted state
The database stores the initial snapshot, current snapshot, and ordered actions. Each successful mutation commits its event and resulting state in one transaction. Reopening and replay verify the stored history against the current snapshot.
The source JSON path and original bytes are retained with a SHA-256 fingerprint. Source edits block further mutations; create a new workspace for a changed source. Replay of historical events still works from the embedded source snapshot, while projections report that the live source is no longer current.
Decision authority
The Unix effective UID at initialization owns the workspace. Action JSON cannot claim another identity. Anyone who can alter the database as that OS user is inside the trust boundary; this is not a distributed authorization service.
The source policy controls requirements and allowed traces. The workspace controls the selected binding, proposals and evidence. Accepting a binding never modifies source specifications. A human report can challenge an accepted binding without silently replacing it.
Recovery and limits
A failed action commits neither event nor state. An interrupted uncommitted SQLite transaction rolls back. If a response is lost after commit, inspect show or replay before retrying: duplicate proposal and decision effects are refused.
Inputs and snapshots are limited to 1 MiB; a model has at most 128 traces of 32 events, and a workspace has at most 256 proposals and 1,024 evidence records. The lock wait is five seconds. Schema/profile mismatches fail explicitly; no automatic migration exists in v1. SQLite commits rely on the local filesystem honoring synchronization. Power-loss durability and network filesystems have not been independently tested.
Derived HTML and JSON exports only create new files. They refuse overwrite and are not an import or source round-trip format. Archival writes remain disabled.
CLI reference
Shipped surface (v1 tracer bullet — persistent local design-review workspace):
funkalization init DB SOURCE.json # create workspace; SOURCE bytes are fingerprinted immutable inputs
funkalization apply DB ACTION.json # apply a propose/evaluate/report/decide action in one transaction
funkalization show DB # print current replayed state
funkalization replay DB # reduce all events from genesis and verify digests
funkalization view DB [NEW.html] # read-only JSON projection; with a path, write an inspectable HTML view
funkalization export DB NEW.json # derived export (explicitly not a round-trip channel)
funkalization archive DB # fails closed — archive/recovery is separately tracked (funkalization-91v)
funkalization tracer NEW_DIRECTORY # self-checking tracer: fixtures, protocol run, machine-readable evidence
Successful operations exit 0 even when a recorded evaluator verdict is fail/unknown; invocation and decision errors exit 2. The tracer exits nonzero when expected outcomes do not match.
--version prints funkalization <semver>; unknown commands exit 2.
Semantics are normative in the
design-tracer spec delta
and the implementation contract.
Action JSON examples
apply uses a tagged JSON object. Unknown fields are rejected. The seed and binding shapes are illustrated in fixtures/pilot/workspace.json and fixtures/pilot/compatible.json; src/model.rs defines the serialization types.
To propose a binding, save an object with "action": "propose", a unique "id", "base_revision": 0, and "binding" containing the complete binding object. Propose preserves the accepted state. Then evaluate and decide with separate files:
{"action":"evaluate","proposal_id":"replacement"}
{"action":"decide","proposal_id":"replacement","base_revision":0,"decision":"accept"}
Each successful call exits 0 and prints the resulting state. Acceptance requires both current checker passes and no declared failure. Use "decision":"reject" to record rejection without changing the accepted revision. Repeating a completed decision exits 2 with decided; a stale revision exits 2 with stale.
A declared report can be added even after acceptance:
{"action":"report","proposal_id":"replacement","verdict":"fail","note":"Observed shipment before durable persistence"}
A successful report exits 0 and preserves the accepted binding and revision. It remains separate from checker evidence; a later counterexample is displayed as a challenge, not silently discarded or used to rewrite history. Reports on retired proposals do not transfer to newer bindings.
Output and failure contract
Errors are JSON objects with code and message on stderr. No action accepts an authority field; the host obtains the Unix UID. Relevant codes include invalid_json, invalid_id, limit, version, unauthorized, source_changed, not_found, duplicate, decided, stale, unresolved, integrity, database, destination and unsupported.
archive DB always exits 2 with unsupported; it never invokes the unsafe archive companion. A derived export is not an archival substitute and always declares that round-trip source writes are unsupported.
Release 0.1.0
- Version: 0.1.0 (from Cargo.toml)
- Build ref: 51a895d
- Generated: 2026-10-20
Status
Experimental: a persistent local tracer is implemented. The full visual-design contract is not complete. The implemented profile uses Rust and SQLite, with explicit finite payment–storage fixtures.
Implemented and tested
- Typed propose/evaluate/report/decide actions, Unix-owner and source-fingerprint guards.
- Atomic state/event commits, replay integrity, restart and process-death recovery tests.
- Separate nominal and behavioral verdicts, finite witnesses, unknown/unsupported outcomes, historical evidence and later counterexamples.
- Read-only JSON/HTML projections, new-file-only derived exports, and explicit archive refusal.
- A cold-start tracer and cross-process replay test, including an actual model substitution.
Broader gate status
| Gate | Evidence and remaining scope |
|---|---|
| G0 | M2: 16 tests; M3: 50 tests and 2,801 event sequences; M4: 18 tests reproduced. Six input hashes verified. Two historical integration archives remain missing. |
| G1–G4 | Implemented for the declared v1 profile. General edits, decomposition and M3/M4 equivalence are not claimed. |
| G5 | Separate installed Specodelic 0.9.0 and 0.10.0 probes with OpenSpec 0.19.0 demonstrated additive overwrite, historical overwrite and partial writes in disposable repositories. Source writing is disabled. |
| G6 | Read-only source-addressed projections exist for the single-source model. A multi-file corpus adapter is not implemented. |
| G7 | No interactive semantic editor or text/UI equivalence result. |
| G8 | The local tracer is runnable; it does not complete every historical C01–C08 claim. |
Evidence and provenance
research/evidence/baseline-results.json records exact historical commands and output. research/evidence/archive-probe.json records the external CLI failures. The current profile and requirement-to-test matrix live in openspec/changes/add-persistent-tracer/.
The finite corpus and separate temporal assertions were authored during implementation. They are not an independently authored human oracle. The user volunteered as first pilot reviewer; no completed walkthrough, comparative timings or adoption results are claimed until their response is recorded.
No deployed openspec/specs/ are claimed. The change remains reviewable under openspec/changes/ until release/deployment and archive are authorized.
design-tracer Specification
Purpose
TBD - created by archiving change add-persistent-tracer. Update Purpose after archive.
Requirements
Requirement: Explicit local authority
The system SHALL preserve imported policy bytes and permit workspace mutations only by the recorded local OS principal against the unchanged source fingerprint.
Scenario: Source changes after import
- GIVEN a workspace initialized from a policy file
- WHEN that file is edited before a proposal or decision
- THEN the mutation fails with source_changed and no committed event or state change
Scenario: Caller supplies an authority field
- WHEN an action includes an unknown authority field
- THEN strict decoding rejects it
Requirement: Atomic guarded actions
The system SHALL commit each successful typed action and its state atomically, reject stale or duplicate decisions, and reproduce the entire state by replay.
Scenario: Restart after acceptance
- WHEN a checked current proposal is accepted and the process restarts
- THEN its binding and revision survive and event replay reconstructs the same state
Scenario: Stale or duplicate acceptance
- WHEN a decision uses an old revision or an already decided proposal
- THEN no state or event is committed
Scenario: Interrupted transaction
- WHEN the process dies before transaction commit
- THEN reopening preserves the previous committed state and replay succeeds
Requirement: Scoped finite obligations
The system SHALL evaluate nominal-operation inclusion and bounded observable trace inclusion independently, preserve counterexamples, and never treat unknown or unsupported as pass.
Scenario: Storage loses durability
- WHEN a backend exposes pay then ship without persist
- THEN behavioral evaluation fails with a replayable offending prefix even if nominal inclusion passes
Scenario: Valid durable shipment
- WHEN all closed traces map into the policy prefix language and at least one ships after durable payment
- THEN behavioral evaluation passes with its concrete-event horizon and source/model scope
Scenario: Incomplete or unsupported model
- WHEN a map is missing, closure is absent, a model is empty or exceeds its horizon
- THEN behavioral evaluation is unknown
- AND WHEN shared-event composition is requested
- THEN behavioral evaluation is unsupported
Requirement: Honest evidence and decisions
The system SHALL distinguish declared reports from built-in checker evidence and accept only current proposals with applicable nominal and behavioral passes and no reported failure.
Scenario: Forged or contradictory report
- WHEN a human report says pass without checker evidence
- THEN acceptance is refused
- AND WHEN a human fail coexists with checker passes
- THEN both are retained and acceptance is refused
Scenario: Counterexample after acceptance
- GIVEN an accepted binding with a scoped checker pass
- WHEN a later declared report records a failure against that proposal
- THEN the evidence and original checker result are both retained
- AND the projection labels the current binding proposal challenged without changing its accepted revision
Requirement: Safe derived artifacts
The system SHALL provide source-addressed read-only projections and loss-reporting derived export, refuse destination overwrite, and reject source archival in this profile.
Scenario: Archive attempt
- WHEN archive is requested
- THEN unsupported is returned and source, state and event log remain unchanged
Scenario: Export to existing destination
- WHEN a derived export targets an existing file
- THEN it is refused and the original bytes remain intact
Requirement: Reproducible tracer
The system SHALL run a clean-directory tracer using checked-in, separately specified fixtures, emit expected-versus-actual evidence, and fail if any assertion is unmet.
Scenario: Cold run
- WHEN a reader runs the documented tracer command in a fresh directory
- THEN compatible and incompatible storage, unknown and unsupported checks, stale and duplicate decisions, restart/replay, safe export and archive refusal are exercised
- AND a machine-readable report and inspectable HTML projection are created
Requirement: Bounded input and version handling
The system SHALL reject oversized or malformed JSON and unsupported schema/profile versions with structured errors and no partial accepted mutation.
Scenario: Unknown schema version
- WHEN an input or persisted state uses an unsupported version
- THEN loading fails explicitly rather than interpreting it as the current profile