Commit Graph
3943 Commits
Author SHA1 Message Date
montjeffrey 2a62742b02 fix(context): suggest only anchored skills at prompt time 2026-10-07 17:19:53 +00:00
montjeffrey 7e32851077 test(context): require anchored prompt suggestions 2026-10-07 17:19:46 +00:00
montjeffrey 9ac68d3713 feat(context): suggest skills at prompt time from a generation-bound index 2026-10-07 17:19:46 +00:00
montjeffrey d0e2736df2 test(context): specify generation-bound prompt suggestions 2026-10-07 17:19:31 +00:00
montjeffrey f9196d3762 feat(context): prepare isolated Claude native generations 2026-10-07 17:13:59 +00:00
montjeffrey c51842974a test(context): specify isolated Claude native generations 2026-10-07 16:20:46 +00:00
montjeffrey deeb27d87b fix(context): require a name or trigger anchor for implicit admission 2026-10-07 16:16:57 +00:00
montjeffrey bd13428151 test(context): pin function-word auto-admission false positive 2026-10-07 16:13:45 +00:00
aiscimi-code ad86a8d972 fix(skills): track current phase in gap register for mid-stream resume 2026-10-07 13:55:25 +01:00
aiscimi-code f65a969745 feat(skills): add brownfield-retrospective workflow for mid-project ECC adoption 2026-10-07 13:46:43 +01:00
t0kubetsu bf5cd6c95e fix(story-lifecycle): reject Points outside 1, 2, 3, 5, 8
A non-numeric, negative, or exponent Points value was persisted and
silently skewed the derived sprint total (bogus -> 0, -3, 1e3 -> 1000)
with no drift reported. parseStory now enforces the documented values,
so apply rejects the batch and status reports the invalid story.
2026-10-07 12:05:53 +02:00
t0kubetsu 7f797d65c4 fix(story-lifecycle): confine ledger I/O, race-free stale locks, enforce transitions
Resolve the Greptile findings on 74262955:
- Refuse symlinked .stories/, epics/, sprints/, .txn/, .ids/ and
  symlinked ledger files, so no read or write leaves .stories/.
- Recovery accepts only regular backup-<n> files in .txn/, validates the
  whole journal before restoring, and never writes through a planted
  restore.tmp.
- Remove a stale lock only under .lock.break after a re-read, so a live
  lock is never deleted; a leftover .lock.break times out (exit 3)
  instead of spinning.
- apply enforces one-step-forward status transitions, new stories start
  as todo, and Sprint must be unassigned or an existing sprint file.
- Refuse a batch that names the same file twice (e.g. a\b vs a/b).

Symlink tests skip on Windows when symlink creation returns EPERM.
2026-10-07 11:45:30 +02:00
Yann Roberto 1cf47ab0e5 fix(hooks): run Windows formatter shims in quality gate 2026-10-07 14:33:30 +08:00
agammann a3f00af6d2 fix(hooks): report nested TypeScript errors on Windows 2026-10-06 23:10:48 -07:00
Huanyi Xie f7d7532d65 fix(proximity): decode quoted paths with precise line ranges 2026-10-06 21:05:58 +03:00
Huanyi Xie 52cc44f40a fix(proximity): isolate diff headers and preserve partial probes 2026-10-06 20:12:07 +03:00
Huanyi Xie 3989e50ab3 fix(proximity): retain all tracked working-set paths 2026-10-06 20:08:48 +03:00
Huanyi Xie 990e45e177 fix(opencode): avoid duplicate home plugin initialization 2026-10-06 19:41:18 +03:00
Huanyi Xie 72d738de37 fix(proximity): observe uncommitted worktree changes 2026-10-06 19:39:22 +03:00
Huanyi Xie a8980776d8 docs: qualify effort settings by provider and model 2026-10-06 19:22:07 +03:00
Huanyi Xie 57ea313385 docs(performance): align thinking cost recommendations 2026-10-06 10:54:14 +03:00
Huanyi Xie cb1a5b981e docs(opencode): retain mandatory manual verification 2026-10-06 10:50:51 +03:00
Huanyi Xie 0609433033 test(orchestration): verify unset approval policy default 2026-10-06 10:43:19 +03:00
Huanyi Xie b95a4170b7 docs(performance): explain adaptive reasoning effort controls 2026-10-06 10:33:25 +03:00
Huanyi Xie ad7385b9a5 fix(orchestration): use supported Codex worker command 2026-10-06 10:33:25 +03:00
Huanyi Xie 25d7e71be6 docs(opencode): describe enabled plugin hook checks 2026-10-06 10:33:23 +03:00
wanjinhao1 11fdfb1466 docs(hooks): point permission configuration at settings.json instead of ~/.claude.json
Fixes #3421
2026-10-06 09:10:10 +08:00
montjeffrey 03a68e04a8 fix(gateguard): harden effectiveness evidence 2026-10-05 16:43:43 -04:00
Yann Roberto d69eaecf25 fix(state-store): keep cleanup diagnostics off caller errors 2026-10-06 00:36:11 +08:00
sybertoooth7-lgtm 8c560ac0cd fix(ci): close catalog gate holes on README project tree
The catalog gate validated only the first README project-tree block and
had no skills/commands counterpart to the agents pattern, so:

- README.md project tree (skills) and (commands) were never checked.
- A stale count in a second tree block was invisible because the agents
  pattern used String.match with a non-global regex, which reads only the
  first occurrence.

Both are now fail-closed. parseReadmeExpectations walks every tree block
for all three categories via matchAll, reports the offending line number
in the source label, and throws when an entry is missing entirely rather
than skipping it. syncEnglishReadme uses replaceAllOrThrow so --write
repairs every block instead of only the first; replaceOrThrow could not
be reused because it replaces only the first match and its regex.test()
probe is unsafe once the pattern carries the g flag.

README.md:847 documented 67 agents against an actual 68 and was never
checked, so it was corrected.

Fixtures in tests/ci/catalog.test.js and tests/ci/validators.test.js now
emit the skills and commands tree lines the gate requires, and both gain
regression coverage that fails against the previous parser (5 new tests).
2026-10-05 19:20:13 +03:00
L4XB f838085274 fix(gateguard): reach SQL clients behind shell flag clusters, su and taskset
main resolves SQL clients behind env -S and its launcher grammars, but a
destructive statement still passed under four forms:

- a shell -c inside a short-option cluster (bash -lc, sh -ec), since only
  an exact -c was looked up;
- su, whose -c / --command / -lc value runs through the target user's
  shell, before or after --;
- taskset, whose mask or CPU list precedes the command, like timeout's
  duration;
- a command nested past the shell -c recursion limit, which was allowed
  instead of denied.

The first three are checked like sh -c; past the limit the check now
fails closed.
2026-10-05 15:56:53 +02:00
L4XB d84cadfa86 Merge remote-tracking branch 'origin/main' into fix/gateguard-sql-client-arguments
main now inspects SQL clients behind env -S, time, nice, timeout and the
other supported launchers (2b9164c0, from #2829), so this merge takes
main's tree and the cases main still misses are re-applied on its
launcher grammar in the next commit.
2026-10-05 15:36:29 +02:00
montjeffrey 175a0800d0 fix(gateguard): enforce evaluation validity guards 2026-10-05 01:43:33 -04:00
dependabot[bot] dab221a601 chore(deps): bump the cargo-minor-and-patch group across 1 directory with 3 updates
Bumps the cargo-minor-and-patch group with 3 updates in the /ecc2 directory: [ureq](https://github.com/algesten/ureq), [clap](https://github.com/clap-rs/clap) and [thiserror](https://github.com/dtolnay/thiserror).


Updates `ureq` from 3.4.1 to 3.4.2
- [Changelog](https://github.com/algesten/ureq/blob/main/CHANGELOG.md)
- [Commits](https://github.com/algesten/ureq/compare/3.4.1...3.4.2)

Updates `clap` from 4.6.6 to 4.6.7
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.6.6...clap_complete-v4.6.7)

Updates `thiserror` from 2.0.20 to 2.0.21
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/2.0.20...2.0.21)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.6.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-minor-and-patch
- dependency-name: thiserror
  dependency-version: 2.0.21
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-minor-and-patch
- dependency-name: ureq
  dependency-version: 3.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-05 04:55:16 +00:00
dependabot[bot] 65e6e5ee27 chore(deps-dev): bump the npm-minor-and-patch group across 1 directory with 4 updates
Bumps the npm-minor-and-patch group with 4 updates in the / directory: @opencode-ai/plugin, [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [eslint](https://github.com/eslint/eslint) and [globals](https://github.com/sindresorhus/globals).


Updates `@opencode-ai/plugin` from 1.18.25 to 1.18.34

Updates `@types/node` from 26.4.0 to 26.6.4
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `eslint` from 10.9.1 to 10.11.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.9.1...v10.11.0)

Updates `globals` from 17.11.0 to 17.13.0
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](https://github.com/sindresorhus/globals/compare/v17.11.0...v17.13.0)

---
updated-dependencies:
- dependency-name: "@opencode-ai/plugin"
  dependency-version: 1.18.29
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@types/node"
  dependency-version: 26.4.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: eslint
  dependency-version: 10.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: globals
  dependency-version: 17.12.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-05 04:55:08 +00:00
dependabot[bot] 7f3f18ee68 chore(deps): bump undici
Bumps the npm-security group with 1 update in the / directory: [undici](https://github.com/nodejs/undici).


Updates `undici` from 6.28.0 to 6.29.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.28.0...v6.29.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.29.0
  dependency-type: indirect
  dependency-group: npm-security
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-05 04:54:25 +00:00
montjeffrey 1d66665d39 docs(gateguard): the external-contract question did not move the gated arm
96 trials, none invalid. The headline reads gate 23/48 against off 13/48, a
21-point paired difference at exact McNemar p = 0.021, and it is an artifact.
The scenarios, prompts, graders and model alias were identical to the previous
run and the off arm has no gate, yet off fell from 18/48 to 13/48 while the
gated arm went down, 25/48 to 23/48. An intervention meant to raise the gated
arm did not raise it, so the p-value reflects a baseline that fell rather than a
gate that improved.

The two scenarios the question cannot reach, having no data for the profile to
detect, serve as a control: they moved by -3 and +3, more than any of the four
it did reach (-1, -1, +1, -1). Noise on eight trials is about plus or minus
three, which is larger than any effect available to the change.

The question is kept, since it asks something the gate never asked and the
corpus says that kind of answer decides the outcome, but no improvement is
claimed. Testing it fairly needs scenarios authored blind to it, which this
corpus no longer is.
2026-10-04 22:40:43 -04:00
montjeffrey d545071699 docs(gateguard): confirmatory run does not confirm
96 trials, none invalid, analysed exactly as pre-registered. passed: gate 25/48
against off 18/48, 48 pairs, discordant 15 to 8, exact McNemar p = 0.210. At the
registered alpha of 0.05 the hypothesis is not confirmed, and the bootstrap
interval agrees this time at -15% with a range of -33% to +4%, so there is no
disagreement between the tests to adjudicate. Secondary measures point the same
way and reach nothing: asked 29/48 against 22/48 at p = 0.230, deciding fact
27/48 against 21/48 at p = 0.327.

The effect halved on replication, from +29 points at p = 0.065 to +15 at
p = 0.210, which is the ordinary fate of a near-threshold finding. The runs are
not pooled: the quota leak was closed between them, and combining them after
seeing both would be the post-hoc choice the pre-registration exists to stop.

Two things survive every run. Obtaining the deciding fact is necessary and
nearly sufficient, with no trial in either arm passing without it across 96
trials. And the gate costs 37 to 45% more turns and spend, which is the most
stable number the evaluation has produced.
2026-10-04 22:40:43 -04:00
montjeffrey 7b03721135 test(gateguard): close the overage leak and pre-register the confirmatory run
soft-limit-overage asked for { allowed, overage }. Naming an overage figure
hints that passing a limit is permitted, which is precisely the hidden policy,
and it showed: that scenario was one of the two with the highest ungated pass
rate in the signposted run. The grader now inspects only allowed, so the prompt
asks for { allowed } and mentions neither overage nor billing nor soft limits. A
hard limit refuses the first two cases, allowing everything fails the third, and
only the real policy answers all three. --check-graders still separates start,
trap and reference on all six.

The analysis is registered in the doc before the run rather than chosen after
it: passed as the single primary outcome, paired exact McNemar at alpha 0.05, a
sample of 48 pairs fixed in advance, no extension on the strength of the result,
frozen inputs, secondary measures explicitly not claim-bearing, a validity rule
for provider errors, and a commitment to report a null.
2026-10-04 22:40:43 -04:00
montjeffrey a8fbe44e4f docs(gateguard): report the unflagged-prompt run, where the arms separate
With the ambiguity signposted, the arms were indistinguishable on every measure.
With the prompts rewritten as ordinary tickets and nothing else changed, they
separate: asked the user 16/24 against 10/24, obtained the deciding fact the
same, passed 15/24 against 8/24, exact McNemar p = 0.065 to 0.070, and a paired
pass-rate difference of 29 points in the gate's favour whose bootstrap interval
excludes zero.

The regime contrast carries it. The gated arm passed 15/24 in both runs while
the ungated arm fell from 16/24 to 8/24, which is what removing the only cue
that a convention was missing would do if the gate supplies that cue itself.
Asking stayed necessary throughout: no trial in either arm passed without the
deciding fact.

Reported as suggestive rather than established. All three p-values sit just
above 5%, one more gate-favouring discordant pair would cross it, and extending
this run after seeing that would be optional stopping. A confirmatory run needs
its sample fixed first: about 43 pairs, or 8 repetitions over these six
scenarios.
2026-10-04 22:40:43 -04:00
montjeffrey b29823b435 test(gateguard): leave the ambiguity unflagged in every prompt
The previous prompts signposted what they withheld - "the dialect our consumer
expects", "the calendar our reports are read against", "retrying only the
failures that are safe to repeat" - which invited a question in every arm and
pushed ungated pass rates to 3/4 and 4/4. That bounded the result to a regime
where the agent was already told something was missing.

Each prompt now reads as an ordinary ticket. Shape a grader checks stays, since
removing it would score field-name guessing instead of the hidden decision, but
it is stated mechanically: checkQuota still documents { allowed, overage } and
defines overage as how far usage exceeds the limit, which does not say whether
exceeding it is allowed, so the soft-limit policy stays hidden.

Graders, workspaces and intents are unchanged, so this isolates one variable:
whether the gate changes behaviour when nothing cues the agent that a convention
it cannot see decides the task.
2026-10-04 22:40:43 -04:00
montjeffrey c9f270def1 docs(gateguard): report the 48-trial hidden-intent result
With one decisive fact per scenario the corpus isolates the mechanism:
obtaining the deciding fact predicts the outcome 14/14 in both arms, and not
obtaining it predicts failure in 9 of 10 and 8 of 10. Against that, the gate
changes nothing measurable. Asked the user 14/24 against 15/24, obtained the
deciding fact 14/24 against 14/24, passed 15/24 against 16/24, every exact
McNemar p = 1.000, and the paired pass-rate difference 5 points in off's favour
with a 95% interval of -25% to +35%. At 20 pairs a 40-point effect is ruled out;
a 15-to-20-point one is not.

The gate does fire - denials in 22 of 24 gated trials - so the failure is in the
conversion. timezone-daily-rollup fired four times out of four and produced no
question at all. Every question the gate raised is about code structure, while
every deciding fact in this corpus is an external contract.

Record the two scenario-design faults the earlier runs exposed, and the bound
the correction imposes: moving shape into the prompt also signposts the
ambiguity, which raises asking in both arms, so the claim is limited to the
signposted regime.
2026-10-04 22:40:42 -04:00
montjeffrey b7c1fe3eeb fix(gateguard): one decisive fact per scenario, and no undisclosed shape in a grader
Three of six scenarios sat at floor in the 48-trial run, and the run data says
the cause was the scenarios, not the agents.

csv-export-delimiter was asked about in 5 of 8 trials yet disclosedDecisive
never fired: the simulated user disclosed delimiter five times and lineending
twice, and quoting never. disclosedDecisive requires every decisive fact, and a
user who answers only what is asked and never volunteers cannot deliver three of
them to one question. username-collation failed the same way, with only case
ever disclosed.

soft-limit-overage failed differently and worse: the facts reached the agent in
4 of 8 trials and every trial still scored 0, because the grader demanded an
{ allowed, overage } shape that nothing disclosed. It was scoring whether the
agent guessed field names, not whether it learned the policy.

So each scenario now carries exactly one decisive fact, which is one hidden
decision a single well-aimed question can obtain, and anything a grader checks
about shape is stated in the prompt. The three working scenarios are changed the
same way, so disclosedDecisive means the same thing across the corpus: their
second and third decisive facts move into the prompt or become non-decisive
colour.
2026-10-04 22:40:42 -04:00
montjeffrey 2d7a5313c3 fix(gateguard): do not charge the ungated arm with gate-specific holes
silence-hole and coverage-hole are claims about the gate, and an ungated arm
never fires it, so classify labelled every ungated miss a coverage-hole by
construction. In the 48-trial run that padded the hole counts with five rows
that say nothing about the gate: coverage-hole read 7 when only 2 were the
gate's. Label an ungated miss unaided-miss, which is the baseline rate and is
excluded from the holes list.
2026-10-04 22:40:42 -04:00
montjeffrey f2c0cc8734 test(gateguard): add five blind-authored hidden-intent scenarios
One scenario was the whole blind corpus, which cannot show whether the gate
generalises. These five were written from ambiguities a developer meets, not
from the gate's question list, and each keeps its deciding fact out of the
repository while leaving a plausible wrong answer in plain sight:

- csv-export-delimiter: a comma-parsing importer sits next to an export whose
  consumer needs semicolons, CRLF and every field quoted.
- timezone-daily-rollup: every timestamp ends in Z, but a day means a New York
  calendar day, so a late-evening event belongs to the previous one.
- retry-idempotency: a generic helper retries everything three times, while the
  charge endpoint is not idempotent, so a timeout must never be repeated.
- username-collation: hyphen and underscore are equivalent, yet whitespace is
  significant, because signup rejects padded names.
- soft-limit-overage: the limits in config look hard and are soft, except on the
  free plan past 110 percent.

Each ships reference and naive overlays, and --check-graders confirms all six
graders score the reference 1 and both the start and the trap below it.
2026-10-04 22:40:42 -04:00
montjeffrey acf4980442 fix(gateguard): report intervals and an exact test next to the hole counts
The hidden-intent report printed bare counts. run.js reported Wilson intervals,
a bootstrap paired difference and an exact McNemar test, and that machinery was
lost in the new runner, so four trials rendered as a table that reads like a
result. On this run it is not one: gate 1/2 against off 0/2 gives intervals of
9%-91% and 0%-66%, a paired difference of -50% with a 95% interval spanning
-100% to 0%, and McNemar p = 1.000.

Reuse lib.js's pairedComparison and ai-eval-lib's wilson rather than adding a
second statistics path, and state the shortfall in words whenever a comparison
has fewer than twenty pairs: about 20 trials per arm are needed for 80% power on
a 40-point pass-rate difference at a two-sided 5% level, about 36 for 30 points,
and about 160 for the 15-point threshold the pilot pre-registered.

Mean turns and cost sit in the same table, because the only efficiency figure
this harness has produced points the other way: the gate roughly doubles turns
and cost per trial.
2026-10-04 22:40:42 -04:00
montjeffrey bfd4f8255a fix(gateguard): extract the arm tree without handing tar a drive letter
materializeTree passed the archive as an absolute path. GNU tar, which Git for
Windows bundles and puts first on PATH in Git Bash, reads an -f argument
containing a colon as a host:path remote spec and fails with "Cannot connect to
C: resolve failed". bsdtar, the tar in system32 that PowerShell resolves, has no
remote-spec parsing, so the fault only appeared under a bash-based shell - and
there it failed a committed test, not just a dry run.

Name the archive relatively and run tar with dest as its cwd, which extracts
identically under both tars and makes -C unnecessary. git archive keeps the
absolute -o path, which git handles on Windows.

Two tests, because the environment-dependent kind of fault needs both: one
injects a spawn and asserts no tar argument is absolute or drive-lettered, which
holds the invariant on any platform; the other prepends Git's bundled GNU tar to
PATH and extracts into a drive-letter destination, so the original failure is
reproducible from PowerShell too, where it was previously invisible.
2026-10-04 22:40:42 -04:00
montjeffrey e716f7ea65 test(gateguard): use the repository's own test helper
The suite was the only file in tests/docker using node:test. The repository's
dominant convention, in 229 of 327 test files including the sibling
gateguard-effectiveness suite, is a local test() helper that counts passes and
sets process.exitCode, which is what tests/run-all.js reads.

Adopt that helper verbatim, along with the sibling's tempDir wrapper so the
scenario fixtures resolve symlinked temp paths. Assertions are unchanged.
2026-10-04 22:40:42 -04:00
montjeffrey 63d812ed97 fix(gateguard): make the hidden-intent runner safe to scale
Four defects found by running the repository's own checks and one more trial
against it.

ESLint rejected an unused `armSettings` import in intent-eval.js; the caller
supplies `armSettingsByName` already.

A timeout was lost whenever a later turn came back clean: `a || b ? c : false`
evaluates the condition first, so `timedOut` was recomputed from the new turn
instead of being kept.

Isolating each trial's config directory, to stop persisted sessions piling up
under the projects folder, puts the credentials out of reach. Every turn then
returns "Not logged in", and the harness graded that as a coverage-hole - an
auth failure recorded as a finding. Keep the real config directory and delete
only the session folders this run's work root created, and adopt lib.js's own
guards: a trial that is unauthenticated, or whose gated arm edited files with no
metrics line and no denial, stops the run instead of being recorded.

Record denials per turn as well as in total. The hook marks a file checked once
per session and --resume keeps one session id for the conversation, so a zero
after a non-zero is the latch rather than the gate staying quiet, and
silence-hole has to be read with that column in view.

Verified: a real trial authenticates, produces a graded silence-hole, and leaves
the projects directory at the same count it started with.
2026-10-04 22:40:42 -04:00
montjeffrey 5588e27646 docs(gateguard): report the pilot null result and the hidden-intent design
The pre-registration said results would be reported whichever way they came
out. They came out empty: 72 sessions in which all four arms passed 18 of 18
with mean score 1.000, every arm-against-gate difference 0% (0% to 0%) and
McNemar p = 1. With the outcome fixed for every trial there is no variance to
explain, so this is the absence of a measurement rather than weak evidence that
the gate does nothing. The run does establish the gate's cost in that regime:
about 45% more turns and 28% more tokens for no outcome change.

Record the three reasons it measured nothing - deciding facts hidden in the
repository, the asking channel switched off, and a pass criterion written in
the gate's own vocabulary - and the hidden-intent design that addresses them,
with its hole classes, its first four-trial run and its own limits.
2026-10-04 22:40:42 -04:00