Repository guidance for coding agents and maintainers working in
solverforge-ui.
README.mdis the source of truth for shipped public API and runtime contracts.WIREFRAME.mdcan include shipped and planned UI, but every section must clearly distinguish which is which.js-src/andcss-src/are the editable sources.static/sf/contains the generated bundled assets served to consumers.static/sf/sf.jsandstatic/sf/sf.cssare compatibility paths. The versionedstatic/sf/sf.<version>.jsandstatic/sf/sf.<version>.cssfiles are generated release artifacts and must stay in step with the crate version.
- Crate version:
0.8.0. - Versioned asset outputs are emitted as
static/sf/sf.<version>.cssandstatic/sf/sf.<version>.js. SF.versionin the bundled JavaScript andassets::version()report the crate version that produced the embedded asset set.- Stable bundles use a short cache lifetime; versioned bundles, vendor files, fonts, and images use immutable caching.
solverforge_ui::assetsis available without default features; the Axumroutes()adapter is available behind the defaultaxumfeature.
assets::get(path)accepts only strict/sf-relative paths and returnsResult<UiAsset, AssetError>.- Empty, absolute, backslash, duplicate-slash,
.and..paths are invalid and returnAssetError::InvalidPath; valid missing paths returnAssetError::NotFound. assets::paths()returns all embedded file paths in stable sorted order.- Axum hosts should use
solverforge_ui::routes()rather than duplicating asset lookup, MIME types, or cache policy. Non-Axum hosts should useassets::get().
createJob()results are normalized before any stream is attached. A valid result is a non-empty string id, a finite numeric id including0, or an object with a scalarid,jobId, orjob_idfield. Non-scalar ids are rejected rather than stringified.- Startup streams may begin with either a scored
progressevent or a scoredbest_solutionevent. progressis metadata-only and must not carry the solution payload.best_solutionmust include bothsolutionandsnapshotRevision.- If a backend seeds startup state from a retained snapshot, it must not emit an
identical duplicate startup
best_solutionimmediately after that bootstrap. deleteJob()is mandatory for every backend passed toSF.createSolver().delete()is terminal-only destructive backend cleanup, and local retained state is cleared only after terminal synchronization and backend deletion both succeed.COMPLETEDandTERMINATED_BY_CONFIGretained jobs require successful terminal snapshot synchronization beforedeleteJob()is allowed.- Paused and terminal lifecycle events remain authoritative;
SF.createSolver()synchronizes retained snapshot state before invoking the corresponding callbacks. - Snapshot callbacks must tolerate a missing snapshot. Render only when
snapshot && snapshot.solutionexists, and synchronize lifecycle markers in afinallyblock so cancellation or snapshot failure cannot hide terminal state. onProgress(meta),onPauseRequested(meta), andonResumed(meta)receive metadata only.onSolution(snapshot, meta),onPaused(snapshot, meta),onCancelled(snapshot, meta), andonComplete(snapshot, meta)receive a snapshot when one is available.onFailure(message, meta, snapshot, analysis)may receive null snapshot and analysis values.onAnalysis(analysis, meta)runs when terminal or paused synchronization obtains analysis;onError(message)reports transport and synchronization failures without inventing a lifecycle transition.- Applications should expose authoritative state on their root after every
callback with
data-job-id,data-snapshot-revision, anddata-lifecycle-state; these markers are observability hooks, not a second lifecycle implementation. - HTTP
EventSource.onerrorrepresents transport state. Reconnecting errors are ignored; a closed stream is surfaced throughonErrorand preserves the last authoritative lifecycle, retained job id, score, metadata, and snapshot revision. In-flight states must remain exact:PAUSE_REQUESTED,RESUMING, andCANCELLINGmust not collapse back toSOLVINGorIDLE. Stop remains visible duringCANCELLING; activating it may reattach a closed stream to listen for the terminal event, but it must not send a duplicatecancelJob()call.
SF.rail.createTimeline()is the shipped dense scheduling surface. Keep its README API reference,WIREFRAME.md, tests, demos, and generated assets synchronized whenever timeline config, geometry, scrolling, or layout behavior changes.zoomPresetsdefaults to['1w', '2w', '4w', 'reset'];[]intentionally removes zoom controls for fixed-horizon app surfaces.- Detailed timeline items must preserve exact interval geometry. Adjacent intervals stay visually disjoint on one track; true overlaps are packed onto separate track rows.
- Dense schedules use one scrollable body viewport with synchronized horizontal header/body movement. Do not document the body scrollbar as hidden.
- Timeline layout must resynchronize after detached
createTimeline()orsetModel()calls once the element is mounted. - Timeline minute fields, day indexes, day counts, ticks, and viewport bounds are finite integer values. Consumer code owns timestamp and timezone normalization before passing data to the timeline.
- Overview
summaryfields are additive. Mixed summarized and raw items keep derived counts and tone data where they remain knowable; explicit aggregate values are required when a summary overrides the inspectable item count. clusterIdidentifies one overview group per lane. Reusing it for disjoint groups is invalid, and expansion must use the returned timeline API rather than mutating component DOM.
- Keep public API changes synchronized across code,
README.md, runnable demos,WIREFRAME.md, the matching skill references, and tests in the same change. - Edit
js-src/andcss-src/, then runmake assets; never hand-edit stable or versioned files understatic/sf/. - Do not hand-edit
CHANGELOG.mdfor ordinary work; release notes are generated bycommit-and-tag-versionthroughmake release-tag. - Do not document planned or exploratory wireframe ideas as shipped behavior until they are wired into the generated assets and the README API reference.
- Keep
skills/solverforge-ui/synchronized with the public API. When a shipped component, lifecycle rule, timeline/model contract, or integration path changes, update the matching reference file andskills/README.mdin the same change.make install-skillinstalls the skill for coding agents. - The skill installer is copy-only and per-harness. It never creates symlinks. It updates or uninstalls only copies with a valid ownership receipt and an unchanged payload; unmanaged or locally modified copies stay untouched.
- Prefer
make lint-frontendfor focused JavaScript linting,make test-frontendormake test-browserfor focused frontend validation, andmake test-quickormake testbefore release work. make bump-version VERSION=x.y.zsynchronizes Cargo metadata, runtime version strings, documentation, skill metadata, and generated versioned bundles. Run the release tool only after the version surfaces and release checks are clean.- When the Rust crate feature surface changes, validate both default features
and
--no-default-features; the latter must keepsolverforge_ui::assetsavailable without depending on Axum.