Session-start 'swamp model search --json' in the AGENTS.md template is unfulfillable - wire it to a hook or drop it
Flaky property test: analyzeExpression dot/bracket agreement fails when the generated model name is a CEL keyword
Accept usernames for user subjects in grant files
Docs: enable-managed-config should say to re-run config migrate when its push fails
datastore config migrate checks the sentinel in the stale local cache and re-runs a full migration over a shared S3 datastore
datastore config migrate writes its sentinel before pushing; after a failed push a re-run reports 'already completed' and never publishes
extension quality, fmt --check and push disagree: bare-specifier severity, cache hit skips gates, gate failure hides the rubric, fmt ignores project config
Docs: grant file reference shows a stale format and omits resources and subjects
Docs: extension push dry-run reference and how-to need the registry checks, API-call list and content hash from Lab #3016
Flaky property test: analyzeExpression hyphenated dot access fails when modelName generates a CEL keyword ("in")
extension quality and push crash with IsADirectory when additionalFiles lists a directory
extension push: a required global argument renders with a stray "" ("text": "string""")
Resolve non-admin actors in dashboard audit logs
A nested structural swamp still waits on its run's lock when the lock holder is not a same-host ancestor (cross-host worker, --server into a non-ancestor serve)
Docs: extension publishing reference is stale on bare specifiers, quality output and fmt config (swamp-club#3022)
Docs: serve authorization page should cover expression-reference checks and the vault.get trust boundary
swamp-extensions: replace bare import specifiers with npm:/jsr: specifiers in first-party extensions
Docs: channel-aware extension push prompts and promote with a manifest (follow-up to #2939)
extension push: 'Requested visibility' line prints to stdout without the logger prefix, twice per run
A nested structural swamp that outlives the run that started it keeps skipping that run's lock while the holder writes
Show signal waits to remote clients and the dashboard
Settle expired signal waits from swamp serve
Resume signalled workflow runs automatically under swamp serve
extension push/quality: structured acceptances for agents, a clearer title than For next time, and an optional reason
Deliver workflow signals through a write-once outcome record instead of writing the run record
Shell steps: a vault.get on a continuation line of a multi-line double-quoted string inside a here-doc is wrapped in quotes
extension push: testing-completeness warns once per generated model (262 warnings in 25 extensions) with no way to mark a package generated
extension push: declare accepted lint warnings where the finding is (inline swamp-review-ignore comments, a sidecar review file beside the manifest), reported in quality, push and the registry
Docs: manual describes the old extension push path resolution (manifest argument, --extensions-dir scope, global skill fallback) after swamp-club#3018
extension push: --extensions-dir is ignored for bundled workflows
extension push: sub-directory extensions cannot be found from outside their directory (manifest path, directory argument, --extensions-dir for workflows and skills)
GCP bigquery datasets update cannot target the dataset: reads the id from a name field datasets do not have
extension push: the undecided private-entitlement verdict still says the registry may start a trial at publish
seed:collective cannot produce a superseded personal trial or a returned trial, so the UAT cannot cover them
Replayable state fragments: Git-reviewable desired state for resources changed by model methods
Extension bundler rewrites zod import text inside string/template literals, breaking code-generating extensions
s3-datastore: pull never removes files deleted on the remote, so a peer's next push re-uploads them and undoes `swamp data gc`
swamp-testing withMockedCommand: record env and cwd in CapturedCommandCall
Forward held-lock identity over --server so a loopback nested swamp does not wait on its caller's lock
extension push: registry metadata and content hash label files relative to the repo dir, so a push with --extensions-dir records ../ paths
Share dashboard authentication across tabs for deep links
Docs: vault.get shell references are placed per occurrence, and every single-quoted use is warned about
A nested structural swamp on a same-host remote worker waits on its own step's lock held by swamp serve
Scheduled flaky-test detection is failing
Docs: declared acceptances for extension push warnings (quality.yaml sidecar, swamp-quality-ignore comments, For next time report)
Docs: describe the private-entitlement registry check and the refusal wording in the extension publishing reference
Fail fast when nested structural commands under parallel runs wait on each other's locks
Use whoami entitlement to explain private-extension push refusals
registry: store and show declared acceptances sent in contentMetadata.acceptances at confirm (swamp-club#3021)
@swamp/digitalocean/space-key: update and sync call /v2/spaces/keys/undefined
knowledge-base retrieve persists query output into the state resource, with lifetime infinite and the query text in the instance name
Docs: remove followUpActions from the MethodResult reference in the extension model manual
signal_test: already-settled test fails by chance when a random UUID spells the sender name
Add a wait_for_signal workflow step that pauses a run for a JSON message
Deliver workflow signals through swamp serve
Docs: serve authorization of expression references, env and direct-type runs (swamp-club#2786)
run doctor through serve interrupts a live run of another serve instance when no heartbeats are recorded
extension push --dry-run: authentication verdict prints 'Signed in as .' when the credential is an API key with no username
Docs: dispatch-env-allow and how remote shell steps receive secrets (swamp-club#2760, swamp-club#2791)
Flaky test: WalSink replayed segments are delivered before events written after replay leaves one WAL segment
Remove followUpActions: no model or extension can produce them
Workflow schedule watcher and workflow edit symlink lookup ignore the managed config workflows dir
main is red: repository_dirty_coverage_test fails for UnifiedData.collectGarbage(orphaned deferred write)
isProcessGone always reports alive on Linux under the test task permissions, failing 9 tests on main
Flaky test: WalSink replayed segments are delivered before events written after replay
serve: relative tls cert-file/key-file and --config resolve against the working directory, not the repository
serve --remote-only: step output from workers is never persisted to the run log (history logs always empty)
findBySpec/findByTag lose a workflow step's output after a version is deleted, pruned or rolled back
extension push --dry-run skips auth, collective and version-exists checks, and prints 'No API calls were made' while calling the registry
serve: a failed pull in acquireModelLocks leaves the per-model lock held until serve exits
serve HA: worker enrollment on a replica that lacks the enrollment token's definition may create a second definition (unverified)
serve HA: pull a token's definition on an auth miss so a peer accepts a new token at once
serve HA: a server token minted on one replica is rejected by the other replicas until they restart (auto-definitions/ is only pulled at boot)
data_query_stale_limit_test fails on Linux: a limited page returns names in b, a order
extension push: version-drift check reports 'no previously published version found' when the registry call failed
Run tracker keeps some interrupted workflow rows forever: retention waits for markSettled, which several paths never call
run doctor reads every run record on each run to rebuild the run indexes
Search and list output follows directory iteration order in the definition, workflow, vault and output repositories
extension push: accept attestation-backed adversarial-review evidence (lookup by extension and content hash), generated provenance, and carry the evidence to the registry at confirm (design: Lab #3023)
verify-reviews: whole-extension rubric review for changed hand-written extensions, carried in the attestation and read by publish (design: Lab #3023)
Docs: step and job name rules in the workflows reference
Design: verify the extension push adversarial review through the attestation (whole-extension rubric review as a local verification step, carried in the attestation, read by publish and the push gate)
Workflow load errors print the raw Zod issue dump instead of a readable message
Decide a stricter naming rule for workflow step and job names
Datastore rework Phase 2: find, wrap and warn on hooked writes outside a unit of work (route 2), without removing it (no behaviour change)
stagecraft: read records with data query --single once it ships
Serve audit: WAL replay at startup deletes segments before the store confirms them
Datastore rework Phase 2: one flush path, so every push is a root's flush or checkpoint (no behaviour change)
Flaky test: usePreviewFetch in-flight fetch for A is discarded after switching to cached B
Serve audit: delivered WAL segments are only deleted at shutdown, so a long-running serve fills the WAL
Serve audit: sink filter config in serve.yaml is never validated
Serve audit: two sinks with the same name share one emitter cursor, so the second never receives events
Serve audit: a durable sink write that times out is retried while the first call is still pending, so the store can hold duplicate sequences
Datastore rework Phase 2: root units can checkpoint, and the CLI token commands push mid-command through it (no behaviour change)
extension push: restore --yes waiving warnings (revert the 20261005.154947.0 flag split), keep the accepted-warnings record, --accept-warnings stays as an optional alias
Flaky test: ModelResolver data accessors for a renamed model return the earlier id's record from findBySpec
serve: workflow trigger get/set ignore --config and read/write <repo>/.swamp/serve.yaml, so they disagree with the scheduler
serve --hot-reload: a changed trigger override schedule is logged as applied but the built-in cron keeps firing
serve: a suspended run with an expired gate from a previous serve instance cannot be cancelled by any path
server tokens: concurrent mints of the same name can pair one mint's record with the other's secret, so a credential authenticates as the wrong principal
Docs: data query reference lists a --limit default of 100 and omits --single
data query --limit returns fewer live records than exist and reports limited: false when stale catalog rows fall inside the limit
data get: help example passes --run latest, which is not a recognised run id
Datastore rework Phase 2: remaining serve direct pushes commit through root units (no behaviour change)
macOS autoupdate LaunchAgent fails with EX_CONFIG after self-update; doctor install still reports HEALTHY
workflow reject leaves sibling gates waiting_approval and dependents pending in a failed, retryable run
extension push: credentials-sensitive-field has no word boundaries and flags secretName, TokenReference, credential_id (fourth fix to the rule)
serve: run scheduled workflows concurrently (per-workflow serialization), and report queue delay
Show expired approval gates as expired, with a Cancel action in the dashboard
swamp-extensions publish.yml: pass --accept-warnings alongside --yes now that --yes no longer waives warnings
Serve audit: emitter breaks the durable hash chain and drops WAL events when a non-durable sink fails or lags
digitalocean codegen leaves an orphaned security_secret.ts model file after its endpoint left the spec
extension promote accepts a yanked version and reports it promoted
workflow cancel cannot cancel a suspended run whose workflow file was deleted, and cancel --all silently skips it
Datastore rework Phase 2: serve handlers commit through the unit of work (no behaviour change)
CEL data.version, data.listVersions and data.findBySpec drop data written before a model instance rename
Docs: doctor install reports a stale or failing autoupdate scheduler
Datastore rework Phase 2: CLI commands stop marking and pushing directly (no behaviour change)
A job alone in its level starts after the abort, and its unstarted step is recorded as a real failure instead of settledByAbort
workflow approve racing workflow cancel loses the cancel: the run returns to suspended after cancel reported cancelled
Datastore rework Phase 2: commit pushes through a root unit of work per command or request (no behaviour change)
extension push: --accept-warnings separate from --yes, and --json exits non-zero instead of skipping the warnings prompt
workflow approve/resume hint lines wrap + quote names, breaking copy-paste
Datastore rework Phase 2: use cases open and commit the unit of work (no behaviour change)
extension push: workflows listed with a shared directory prefix collapse into one workflows.yaml; all but the last are silently dropped
digitalocean codegen: app-platform and database spec secret fields are not marked sensitive
dashboard: approved autoResume run shows Resume and needs-inputs hint while serve already resumed it (status running)
repo upgrade does not remove superseded skills from the repo-local skills dir, so its own warning never clears
readModelData silently drops data written before a model instance rename
Docs: manual approval pages show old quoted approve/approvals hints
Docs: extension push --accept-warnings is separate from --yes (manual pages)
data query: does not follow data rename forwarding that data get follows
vault migrate: report missing required --config fields clearly, before installing the target type
Unit tests share fixed /tmp catalog paths, so concurrent runs corrupt them and fail every later run
extension push output: double-quoted adjacent interpolations, repo-relative paths, and --json emits three JSON documents
vault read-secret: to a pipe or file, drops the last line of a multi-line value when that line is 1024+ chars with no trailing newline (exit 0)
Docs: vault create and vault migrate name missing required --config fields and prompt for them
auto-resolve: the Installing line prints the extension's entire multi-line description
s3-datastore/serve: readResource without a version returns an older version; the latest marker is rewritten backwards
Publish agent-facing projections of the manual: /llms.txt, .md pages, and llms-full.txt
CLI tells signed-in callers to sign in when the registry rate-limits them
Let agent-runner drive more agent CLIs (Kilo Code and others)
Datastore rework Phase 1 close-out: lock repository marks at zero, remove dead helpers, document the end state (no behaviour change)
Installer and README present required Swamp Club login as optional
s3-datastore: every fast-path miss re-downloads every _index shard, so a busy serve pulls the whole index every poll (~230 GB/day S3 egress)
data query: renamed data items are not found by their old name
data query: model lookup by id and orphan recovery parity with data get
Datastore rework Phase 1 move C2: the vault config repository stages typed changes, serve stops marking vault files by hand (no behaviour change)
Model add-ons that extend a built-in model type never attach in CLI processes
deployments: sync fails after adopt/get because stored state has id, not uid
workflow approve on a run that is not suspended prints a fatal error with a stack trace
data query: no way to target a workflow's latest run
Datastore rework Phase 1 move C1: the workflow run repository stages typed changes (no behaviour change)
data query: binary content lacks contentEncoding/base64 parity with data get
data query: no fallback from spec name to data instance name
data query: no definitionHash or garbageCollection fields in query results
Add a subprocessors page at /subprocessors
Feedback: Swamp as an approval-gated control plane for a small fleet
stagecraft: publish @swamp/stagecraft: manifest, Lab exclusion, real-tree checks, packaged install, merge
Datastore rework Phase 1 move B: definition, workflow and evaluated repositories stage typed changes (no behaviour change)
Add self._index to forEach expression context
Datastore rework Phase 1 move A: data and output repositories stage typed changes (no behaviour change)
A nested structural swamp spawned by swamp serve skips every lock serve holds, including other runs' locks
stagecraft studio work-item page: follow-ups from the #2944 reviews
gatorwalk-factory studio: Simulate follow-ups from #2808's reviews (stuck Run tab on an unrunnable walk, settling states)
stagecraft studio work-item page: follow-ups from the #2956 reviews
data query: confirm catalog parity with data get for custom datastores
Docs: document self._index for forEach steps in the manual
data query: single-result --json mode for scripts migrating from data get
stagecraft: stop relying on the CLI's swamp data get
extension push: Credentials & Secrets review flags token-count fields (inputTokens, outputTokens, totalTokens) as secrets
stagecraft studio: serve refuses to start without queryData, and printed start commands can coerce a title
Datastore rework Phase 1: optional ambient unit of work in repositories (no behaviour change)
stagecraft studio: a Ticket tab on the work-item page (description, comments, relations), and the nav shows where you are
extension push: eval()/new Function() safety check is a substring match and blocks obj.eval( method calls in bundled libraries
Extension content extractor truncates metadata when a string, comment or regex contains an unpaired brace
Datastore rework Phase 1: UnitOfWork port and legacy adapter (no behaviour change)
Versions written by different steps of one workflow run all stay isLatest=true (data query / readModelData / queryData return stale "latest")
`swamp repo upgrade` adds its canonical Claude audit hook next to an existing variant, so every command is recorded twice
Docs: document data query --single and fix the stale --limit default in reference/data.md
acquireModelLocks writes SWAMP_LOCK_HOLDER_PID to process-global env, so concurrent runs in one process clobber each other
`swamp issue bug` redactor treats the file name `settings.local.json` as a hostname and rewrites it to `[HOST-1].json`
stagecraft: short work-item ids: a tracker prefix plus a counter (blog-12), the ticket's id for external trackers (abc-12), and -2 for later work on the same ticket
data get --workflow silently returns an arbitrary step's data when several steps share a data name
stagecraft studio: long work-item keys overflow Board cards, and the built-in tracker ref repeats the key
Relationships
#2348 Add self._index to forEach expression context
Opened by stack72 · 9/22/2026· Shipped 10/2/2026
Problem
Workflow forEach steps that iterate over file paths cannot use the factory pattern (one model definition per iteration) because file paths contain slashes, which are rejected by both model names and dataOutputOverrides vary values. There is no way to generate a unique, slug-safe identifier per iteration in CEL or template expressions.
The forEach index is already tracked internally (forEachIndex in
execution_service.ts) but is not exposed to the expression context.
Proposed Solution
Expose the zero-based forEach iteration index as self._index in both
template expressions (${{ self._index }}) and CEL expressions. The
underscore prefix avoids collision with user-defined forEach item
variables.
This enables the factory pattern for forEach steps:
- name: process-${{ self.file }}
forEach:
item: file
in: "${{ data.latest('repo', 'diff').attributes.files }}"
task:
type: model_method
modelType: "@some/extension"
modelName: processor-${{ run.id }}-${{ self._index }}
methodName: run
inputs:
data: "${{ data.latest('processor-' + run.id + '-' + string(self._index), 'result').attributes.value }}"Each iteration gets its own model definition — no shared datastore lock, full parallelism.
Implementation
A prototype is working on branch typesafe-triage-integration. The change
is ~3 lines in execution_service.ts:
- Line ~3147: Add
_index: forEachIndexwhen buildingselffor forEach iterations - Line ~953: Preserve
_indexwhenselfis rebuilt after model resolution (the existing code overwritesselfwith model definition fields + forEach vars, losing any extra properties)
Both changes are in DefaultStepExecutor and WorkflowExecutionService.
Alternatives
- Use
dataOutputOverrideswithvary— rejected: vary values cannot contain path separators - Use per-file model names with file paths — rejected: model names become filesystem paths, slashes create nested directories
- Use shared models with concurrency limits — works but causes datastore lock contention warnings at high concurrency
- Add a CEL
replace()function for string sanitization — heavier change, less general
Use Case
The immediate use case is the @swamp/typesafe-ai triage integration in
the verification workflow, where per-file diffs are sent to TypeSafe's Jev
model for noul evaluation. Each file needs its own model definition to
avoid lock contention when running 64+ concurrent iterations.
Shipped
Click a lifecycle step above to view its details.