Skip to content

Agent edit-loop upgrade: reliability, discoverability, prescriptive errors, atomic + corrective writes#39

Merged
lukaisailovic merged 11 commits into
mainfrom
feat/agent-edit-loop
Jun 26, 2026
Merged

Agent edit-loop upgrade: reliability, discoverability, prescriptive errors, atomic + corrective writes#39
lukaisailovic merged 11 commits into
mainfrom
feat/agent-edit-loop

Conversation

@lukaisailovic

@lukaisailovic lukaisailovic commented Jun 26, 2026

Copy link
Copy Markdown
Owner

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

  • CRLF-safe row appendsappendLines now 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)

  • listIslands lists each island's optional field names alongside required (names only — no schema bloat).
  • getOverview returns hints (read-back via previewDataset/runSql({dataset}), patchManifest deep-merge, runActions/runSync).
  • getIslandSchema notes carry copyable examples for the chronically-guessed shapes (drilldown, gauge.goal goals, content.editor).

The validator teaches the shape

  • Structural errors for drilldown / gauge.goal goals / content.editor / select-filter append a copyable correct-shape example (failure-path only; no schema/outcome change). Matches the in-loop examples byte-for-byte.

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 singular runAction (breaking; single insert = runActions([{ action, rows }])).
  • delete / update / replace action modesActionSpec.mode widened to insert | 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 for runSql({ dataset }) to read computed values back.

Polish

  • Keyless connectors are discoverable: status carries canSyncDirectly + a note; a keyless connector (auth: none) syncs directly via runSync — only oauth2/bearer need a human to connect.
  • null vs "" — flat-file writes give a prescriptive hint (CSV stores no null; pass "" or omit for default).
  • Arbitrary currencycurrency:<ISO> value format (e.g. currency:RSD) beyond the built-in usd/eur/gbp/jpy.
  • Local-only telemetry — dry-check rejections + a manifest-resend counter logged under .openislands/ (fail-safe, no phone-home, not a dashboard).

Docs & golden path

  • start.md and the skill gain recipes: atomic multi-write, correct/delete a row, derive-with-SQL. Connector/currency/null docs updated; the full runAction → runActions sweep 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+.

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.
@lukaisailovic lukaisailovic changed the title Agent edit-loop upgrade: CRLF-safe appends, discoverability, prescriptive errors, atomic runActions Agent edit-loop upgrade: reliability, discoverability, prescriptive errors, atomic + corrective writes Jun 26, 2026
@lukaisailovic lukaisailovic merged commit 08567da into main Jun 26, 2026
2 checks passed
@lukaisailovic lukaisailovic deleted the feat/agent-edit-loop branch June 26, 2026 16:59
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.

1 participant