Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
074adc1
Add field limits to the registry: max_length, min_length, min, max an…
rorychatt Oct 29, 2026
d4bdb2b
Refuse values outside a field's limits on every write path with limit…
rorychatt Oct 29, 2026
4f1f043
Read the limit keys in the package manifest and hold migration defaul…
rorychatt Oct 29, 2026
0467d87
Show field limits in the MCP tool schemas and the OpenAPI document (D90)
rorychatt Oct 29, 2026
364ad5c
Take core version 0.2.1 for the new manifest keys, with the pins that…
rorychatt Oct 29, 2026
f61ef5b
Test field limits through the real router, the stores and the binary …
rorychatt Oct 29, 2026
338fde6
Show limits in the back office form: a character counter, bounds and …
rorychatt Oct 29, 2026
e2fab24
Test field limits in the browser end to end (D90)
rorychatt Oct 29, 2026
5034695
Record D90 (field limits) as proposed, amend MODULES.md and CONVENTIO…
rorychatt Oct 29, 2026
7b748e5
Registry: state a limit without the written value, advise the cap, an…
rorychatt Oct 29, 2026
b62c004
Keep limit_violated through an effect, never state the value to an ag…
rorychatt Oct 29, 2026
a6b1f22
Packages: the plan shows the limits a field is held to and which way …
rorychatt Oct 29, 2026
c1fd56a
Test that an inbound connector item over a limit is rejected by itsel…
rorychatt Oct 29, 2026
7974bf6
Counter: tell a screen reader once per band and mark the control inva…
rorychatt Oct 29, 2026
2614388
D90: correct the record after the review of PR 33 (F0, F1, F2, F3, F4…
rorychatt Oct 29, 2026
c4fddd4
MODULES.md and DEPLOY.md: say what the limits do now (D90 review F0, …
rorychatt Oct 29, 2026
81c4fc4
ISSUES and CLAUDE.md: K43 to K45 after the review, a D90 line in the …
rorychatt Oct 29, 2026
f755757
ISSUES K44(b): cite what a real run showed of the schemas that offer …
rorychatt Oct 29, 2026
13810a5
Packages: an installed manifest is read as the previous core read it …
rorychatt Oct 29, 2026
2c4bb9e
Test that a decimal and a pre-flight refusal state no value to an age…
rorychatt Oct 29, 2026
c86e7e7
test(e2e): a stored value over a lowered limit is not marked invalid …
rorychatt Oct 29, 2026
174fc07
Docs: D90 says where a limit is refused with its code and where not, …
rorychatt Oct 29, 2026
d85f832
Docs: D90 K44 and Decisions 4, 8 and 9 say what the code does (second…
rorychatt Oct 29, 2026
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 CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ The owner's rulings of 2026-10-01 (OR-1 to OR-21), 2026-10-27 (OR-22 to OR-34),
- Operations (D59, docs/DEPLOY.md): `obop-keys` (the key ring, the sealed envelope, `reseal` for rotation), `obop-migrate` (schema version and ordered migration steps), `obop-ops` (backup, restore, verify, `secrets rotate`); `obop-storage` also holds the one PostgreSQL connector with TLS (`obop_storage::tls::PgConnector`, D66: `sslmode` and `sslrootcert` in the URL, rustls, plain when unset; every PostgreSQL connection goes through it) and the cross-process lock, counters, expiring values and compare-and-set that make several server processes on one PostgreSQL database behave as one. CLI: `obop migrate|backup|restore|secrets`, `obop platform upgrade-instances`; `deploy/restore-drill.sh`; `.github/workflows/image.yml` publishes `ghcr.io/spacecorps/obop`.
- Operations since D73 (`docs/DEPLOY.md` "Backups" and "Upgrades", `docs/RUNBOOK-RECOVERY.md`): `obop-keys::stream` (the chunked ChaCha20-Poly1305 seal of format 2 backups), `obop-migrate` (the checksummed migration ledger `obop_migration`, drift refusal at start, adoption of older databases, the database id `obop_meta/instance`, stored plans, the token-based DDL classifier `ddl.rs`, `RunOptions::before` where a self-migrating server takes its backup under the migration lock), `obop-ops` (`backup` formats 1 and 2 with a MAC'd manifest and refusal of plain backups where a key is configured, `fsutil` crash-safe private file publishing, `schedule` and `retention`, `sink` (`BackupSink`, `FsSink`), `catalog` (records `obop_backup`, `obop_restore_drill`, `obop_storage_attestation`), `gates` (strict and relaxed migration gates, rehearsal receipts tied to the database id, rollback in one transaction), `drill`, `doctor` for `obop storage check`, `pgadmin` (`ServerInfo`)); `obop-pkg` has the `MigrationGate` port implemented by `backup_cli::ModuleGate`. CLI: `obop migrate status|verify|plan|run|rehearse|rollback`, `obop backup [run|list|prune|verify]`, `obop restore`, `obop drill`, `obop storage check|attest|show` (CLI-only; D73 asks the owner for that exemption); stable exit statuses 3 (`restore_key_required`), 4 (`backup_not_sealed`), 5 (`restore_files_failed`). Settings: `OBOP_BACKUP_DIR|KEY|KEY_PREVIOUS|SCHEDULE|COPY_DIR|COPY_DELETE|KEEP_*|MAX_AGE_DAYS`, `OBOP_HOST_ID`, `OBOP_MIGRATION_GATES`, `OBOP_INSTANCE_BACKUP_DIR` (control plane).
- Decimal and datetime kinds (D72): `obop-values` (leaf crate: `Decimal` as i128 units with strict parser, checked arithmetic with a 256-bit division, `Round`; `Instant` in microseconds, RFC 3339 in and canonical UTC out; `Bucket`; the bundled IANA time zone database via `jiff`, `ZoneDb::fixture` for tests; the one source of the examples and patterns the surfaces print). `FieldKind::Decimal{precision,scale,round}` and `Datetime` live in `obop-registry`; every write path calls `kinds::canonicalise` (`obop-commands/src/kinds.rs`) and the stores accept canonical text only; queries use `Key::Dec`; aggregates are exact (`avg` takes `scale` and `round`); filters and `group_by` take `bucket` (day, week, iso_week, month, year) and `zone`; `OBOP_ZONE` (default UTC) is read only by `App::set_zones_from_env`; a changed decimal scale or smaller precision is a breaking package change that needs a `[[migration]] op = "reformat_field"`. Backoffice: `decimals.ts`, `instants.ts`. Not in the backoffice views yet: decimal measures and datetime or iso_week or year dimensions in pivot, graph, kanban, calendar and dashboards (D72 Not delivered).
- Field limits (D90, PROPOSED, in review): `FieldDef.limits` in `obop-registry/src/limits.rs` (`Limits`, `Bound`, `Breach`, the install gate `problems`, and the four constants `DEFAULT_TEXT_MAX` 2,048, `DEFAULT_LONGTEXT_MAX` 100,000, `TEXT_MAX_CAP` 10,000, `LONGTEXT_MAX_CAP` 1,000,000, whose values are the owner's to rule on); manifest keys `max_length`, `min_length`, `min`, `max` (`obop-pkg` `FieldM`, `bound_of`; a package that declares one writes `requires_core = "^0.2.1"`, and `CORE_VERSION` is 0.2.1); characters are Unicode code points, never bytes; the check is `kinds::check_text_limits|check_int_limits|check_decimal_limits` behind `obop-commands/src/engine/crud.rs` (`check_fields_against`, `check_effect_fields`) and, for the public submit, `store::check_value`; the code `limit_violated` (400) is kept through an effect by `errors::wrap` and left off a bulk row and a public submit; a refusal states the length or the value only to a person who typed it, never to an agent or from an action effect; a stored value that an `update` leaves as it is is not re-checked; upgrade diff kind `field_limits` (`obop-pkg/src/migrate.rs`, `Held`); back office `limits.ts` (`codePoints`) and the counter in `fields.tsx`. Not delivered, with the effect maps, the module builder and the writers that bypass the checks: docs/ISSUES.md K43 to K46.
- System fields and record history (D75, first slice; nothing is tracked yet, so no record gets a system key and the surfaces differ only by two renames: the module list says `upgraded_at` for `updated_at`, and `connection` no longer has `created_at`): `obop_storage::sysfields` is the planner every backend shares (six system keys, no-op writes stamp nothing, `row_version` compare-and-set through `Op::Patch`, `Op::EraseHistory`), memory, SQLite and PostgreSQL keep the history (PostgreSQL: `obop-storage/src/pg_hist.rs`, the table `obop_record_change` partitioned by month with an append-only guard and three `SECURITY DEFINER` maintenance functions, made on first use and not at start; `Backend::changes` is the commit-safe change feed with cursors bound to the epoch and the cluster, plus `history_stats`, `history_ensure_partition`, `history_drop_partition` and the history in backup and restore; docs/DEPLOY.md "Change history and system fields on PostgreSQL"); the command layer, REST, MCP, the CLI and the UI use none of it yet; the reserved names, `ModelExt.history` and its manifest keys are checked by the install gate of `obop-registry`.
- Modules and platform: `obop-pkg` (obop.toml X1 packages: parse, gate, semver and SPDX checks, dependency resolution and install plans, runtime install/uninstall, marketplace; admin commands `module.*`, `module_plan.preview` and `marketplace_listing.*`; D55: one core version `obop_types::CORE_VERSION`, module metadata, versioned dependencies), `obop-platform` (control plane: accounts, instances, provisioners local/sliplane/fake). `packages/*.toml` is the bundled catalog; translations (D52) are `[labels.<lang>]` tables in a package document, bundled as `packages/labels/<lang>/<id>.toml` (de, fr, pl, fi, sv, es; also for the built-in modules), checked by `obop modules check --complete FILE`, templated by `obop modules labels TARGET LANG`. A text field can show the label of an option of a select (`shows_option_of` and `when` in `[[model.field]]`, D52 amendment): `ShowsOption` in `obop-registry` `rules.rs`, the gate `expr::check_display_subset`, `shows_option_of` and `shows_option_when` in `/api/meta`; the browser evaluates the condition (`displayCondition.ts`, `shownOption.ts` in the backoffice), and `displayCondition.fixtures.json` is read by both it and `obop-commands/src/shows_option_tests.rs`. See `docs/DEPLOY.md`; Sliplane project `obop` (`project_2x9d8q51m9lt`, account `spacecorps`) exists and is empty.
- Updates (D63, `docs/DEPLOY.md` "Updating from inside"): every package declares `requires_core` and a range per dependency; `module.upgrade|upgrade_all|rollback` (atomic batch, `[[migration]]` data steps in `obop-pkg::migrate`, D59 lock, audited, agents propose); `obop-update` (release feed, core compatibility of installed modules, `instance_update.check|status|start|rollback`, `obop update ...`) asks the control plane (`obop-platform::update`: per-instance update token, `GET /api/releases`, rollback to the previous image when the new one does not start).
Expand Down
59 changes: 30 additions & 29 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ members = ["crates/kernel/*", "crates/modules/*", "crates/obop"]
edition = "2024"
rust-version = "1.99"
publish = false
version = "0.2.0"
version = "0.2.1"
license = "Apache-2.0"
repository = "https://github.com/SpaceCorps/obop"

Expand Down
3 changes: 2 additions & 1 deletion crates/kernel/obop-commands/src/engine/bulk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,8 @@ pub(crate) fn bulk_create(eng: &Engine, def: &Arc<ModelDef>, input: &Value) -> R
ids.push(None);
failed += 1;
if errors.len() < MAX_LISTED_ERRORS {
errors.push((r.row, r.line, e));
// The list names the row; a refusal with a code (D90) is listed by its text.
errors.push((r.row, r.line, crate::errors::plain(&e).to_string()));
}
}
}
Expand Down
Loading
Loading