Agent edit-loop upgrade: reliability, discoverability, prescriptive errors, atomic + corrective writes#39
Merged
Merged
Conversation
appendLines joined and separated rows with LF regardless of the target file's existing dialect, so appending to a CRLF file (via an action or a connector) mixed in LF and corrupted it. Detect the dialect from the existing content and use it for the separator, the join, and the trailing newline. Adds a CRLF/LF regression test over the public resolveWriter path.
…es pre-flight
Shift discoverability left so agents stop guessing:
- listIslands lists each island's optional field NAMES (not full schemas —
names only, no keystone bloat), alongside required.
- getOverview returns hints pointing at runSql({dataset}) read-back,
patchManifest's section deep-merge, and runAction/runSync.
- getIslandSchema notes carry copyable examples for the chronically-guessed
shapes (drilldown, gauge.goal goals, content.editor file/dir).
… errors When an agent guesses a nested shape wrong, the validator dropped to generic Zod text that names the rule, not the correct shape. Append a copyable correct-shape example to the four chronically-guessed structures — drilldown, gauge.goal goals, content.editor, and the select page filter — on both the generic Zod path and the semantic checks. Failure-path only; no schema, type, or validation-outcome change.
…tion
runActions([{ action, rows }, ...], { atomic }) runs several action inserts as
one unit. atomic (default true) validates every call's rows first and writes
nothing if any is invalid; a mid-batch write failure rolls back the writes
already applied in reverse order — restoring a snapshot, or removing a
newly-created file. Returns per-call { action, inserted, checkpoint_id } plus
checkpoint_ids.
BREAKING CHANGE: the singular runAction(name, rows) is removed — call
runActions([{ action: name, rows }]) for a single insert. The form.entry schema
description is updated to match.
…templates Replace every runAction reference with runActions in the MCP/data docs, the agent skill (and its synced template copies), and the example/template READMEs. Add two golden-path recipes to start.md and the skill: an atomic parent+children multi-write, and deriving a metric with a SQL transform.
…:<ISO> format Widen ActionSpec.mode from the insert-only literal to enum ["insert","replace","delete","update"] (default insert); the match predicate and new values are supplied at call time, not the manifest. Widen ValueFormat to also accept "currency:<ISO>" (e.g. currency:RSD) for any ISO 4217 code beyond the built-in usd/eur/gbp/jpy.
…r status, null-vs-empty copy deleteRows/updateRows/replaceRows rewrite a writable dataset via match→drop/patch→ replace (equality, multi-column AND, mirroring query()'s string compare). Safety: an empty match is rejected before any I/O (no implicit wipe), the dataset is read uncapped so a rewrite can't silently drop rows past the row cap, match/set columns are verified, and the rewrite snapshots first for rollback. listConnectorStatuses now flags keyless connectors (canSyncDirectly + note). validateRows gives a prescriptive null-vs-"" hint (CSV stores no null).
…set, keyless copy, local telemetry
runActions now dispatches by the action's declared mode — insert/replace take rows,
delete takes a match, update takes match+set — keeping the atomic validate-all /
reverse-rollback semantics. previewDataset(dataset) is a findable alias for
runSql({dataset}). The connector API copy clarifies keyless connectors sync directly
via runSync (only oauth2/bearer need a human). Local-only, fail-safe telemetry logs
dry-check rejections + a manifest-resend counter under .openislands/ (no phone-home).
…format formatValue renders a "currency:XXX" format through Intl with that code. Also updates the ConnectorStatus test fixture for the new keyless status fields.
…s, currency codes, null pitfall Document the new action modes + their runActions call shapes, the previewDataset read-back alias, keyless-vs-OAuth connector semantics with an example, the currency:<ISO> format, and the CSV null-vs-"" pitfall, across the docs, the skill, and its synced template copies.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upgrade the agent edit loop
The MCP edit loop is the product and the agent is its user. This upgrade closes the gaps where an agent guessed wrong, distrusted what it couldn't read back, or couldn't correct data — across reliability, discoverability, the maintain loop, corrections, and polish.
Reliability
appendLinesnow detects and preserves the file's existing line-ending dialect (was LF-only, corrupting CRLF files on every action/connector write).Discoverability (shift knowledge left)
listIslandslists each island's optional field names alongside required (names only — no schema bloat).getOverviewreturnshints(read-back viapreviewDataset/runSql({dataset}),patchManifestdeep-merge,runActions/runSync).getIslandSchemanotescarry copyable examples for the chronically-guessed shapes (drilldown, gauge.goal goals, content.editor).The validator teaches the shape
Maintain loop — atomic & corrective writes
runActions([{ action, … }], { atomic })runs several action ops as one unit; atomic (default) validates all first and rolls back every write on any failure. Replaces the singularrunAction(breaking; single insert =runActions([{ action, rows }])).ActionSpec.modewidened toinsert | replace | delete | update. delete ={ action, match }, update ={ action, match, set }(equality, multi-column AND). Safety: an empty match is rejected (no implicit wipe); the dataset is read uncapped so a rewrite can't drop rows past the cap; each rewrite snapshots first for rollback.previewDataset(dataset)— findable alias forrunSql({ dataset })to read computed values back.Polish
canSyncDirectly+ a note; a keyless connector (auth: none) syncs directly viarunSync— only oauth2/bearer need a human to connect.""or omit for default).currency:<ISO>value format (e.g.currency:RSD) beyond the built-in usd/eur/gbp/jpy..openislands/(fail-safe, no phone-home, not a dashboard).Docs & golden path
start.mdand the skill gain recipes: atomic multi-write, correct/delete a row, derive-with-SQL. Connector/currency/null docs updated; the fullrunAction → runActionssweep across docs + templates.Verification
pnpm build && pnpm typecheck && pnpm test && pnpm lint+pnpm validate:templates— all green. Tests: schema 178, compiler 171, mcp 154+, runtime 389+.