Skip to content

feat(miner): add .gittensory-ams.yml operator execution-policy config (#5132) - #5249

Merged
JSONbored merged 1 commit into
mainfrom
feat/ams-policy-spec-5132
Jul 12, 2026
Merged

feat(miner): add .gittensory-ams.yml operator execution-policy config (#5132)#5249
JSONbored merged 1 commit into
mainfrom
feat/ams-policy-spec-5132

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

Advances #5132

runMinerAttempt requires submissionMode/slopThreshold/capLimits/convergenceThresholds, none of which had any config or env backing anywhere in the codebase -- pure policy dials that would otherwise get hardcoded with no way to tune them.

Adds AmsPolicySpec (engine package, a structural sibling to MinerGoalSpec) and resolveAmsPolicy (miner package), mirroring .gittensory.yml's own established self-host precedent (src/selfhost/private-config.ts's makeLocalManifestReader, whose own doc comment says the operator's local file "takes priority over -- and fully REPLACES -- the public .gittensory.yml"): a target repo may propose a .gittensory-ams.yml default, but the operator's own local copy (in their gittensory-miner config dir), when present, fully replaces it -- never a field-by-field merge.

This boundary is deliberate: MinerGoalSpec (.gittensory-miner.yml) is what a target repo wants from being mined (paths, labels, opt-out) -- that's legitimately the repo's own call. AmsPolicySpec is how aggressive the operator wants their own agent to be (budget, submission mode, slop threshold) -- a repo's own checked-in file must never get to loosen what an operator's agent is willing to do against that same repo, since that would let a malicious/compromised repo talk an operator's miner into raising its own risk tolerance. A repo's file can only ever propose a fallback default for an unconfigured operator.

Deny-by-default: submissionMode defaults to "observe" (computes real decisions, never actually submits) and slopThreshold to "low" (strict), matching the codebase's own stated deny-by-default conventions (src/settings/autonomy.ts's AUTONOMY_LEVELS).

Test plan

  • node --test packages/gittensory-engine/test/ams-policy-spec-parser.test.ts -- 9/9 (missing/malformed input, every field's normalization + rejection, JSON/YAML parsing, oversized content)
  • node --test packages/gittensory-engine/test/*.test.ts -- full engine suite, 519/519 passing
  • npx vitest run test/unit/miner-ams-policy.test.ts -- 9/9 (config-path precedence, defaults-when-neither-exists, repo-file fallback, local-fully-replaces-repo regression, never-fetches-once-local-found, malformed-local-file, malformed-repoFullName, network-error, .github/+.json candidate paths)
  • npm run test:engine-parity
  • node --check packages/gittensory-miner/lib/ams-policy.js
  • npm run --prefix packages/gittensory-miner build (added ams-policy.js to the check list)

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 12, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
gittensory-ui b927588 Commit Preview URL

Branch Preview URL
Jul 12 2026, 11:39 AM

Advances #5132

runMinerAttempt needs submissionMode/slopThreshold/capLimits/
convergenceThresholds, none of which had any config or env backing
anywhere in the codebase -- pure policy dials that would otherwise get
hardcoded with no way to tune them. Adds AmsPolicySpec (engine package,
structural sibling to MinerGoalSpec) and resolveAmsPolicy (miner
package), mirroring .gittensory.yml's own established self-host
precedent: a target repo may propose a .gittensory-ams.yml default, but
the operator's own local copy (in their gittensory-miner config dir),
when present, fully REPLACES it -- never a field-by-field merge. A
repo's own file can only ever propose a fallback default for an
unconfigured operator; it can never loosen what an operator's agent is
willing to do against that same repo.

Deny-by-default: submissionMode defaults to "observe" (computes real
decisions, never actually submits), slopThreshold to "low" (strict).
@JSONbored
JSONbored force-pushed the feat/ams-policy-spec-5132 branch from 9a1d1fb to b927588 Compare July 12, 2026 11:38
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent did not find any vulnerabilities or security issues in this PR.

@JSONbored JSONbored self-assigned this Jul 12, 2026
@JSONbored
JSONbored merged commit f92b298 into main Jul 12, 2026
15 checks passed
@JSONbored
JSONbored deleted the feat/ams-policy-spec-5132 branch July 12, 2026 11:41
@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 40.98361% with 36 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.26%. Comparing base (e6adb43) to head (b927588).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
packages/gittensory-engine/src/ams-policy-spec.ts 40.98% 24 Missing and 12 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5249      +/-   ##
==========================================
- Coverage   94.34%   94.26%   -0.09%     
==========================================
  Files         473      474       +1     
  Lines       39982    40043      +61     
  Branches    14576    14598      +22     
==========================================
+ Hits        37722    37747      +25     
- Misses       1585     1609      +24     
- Partials      675      687      +12     
Flag Coverage Δ
shard-1 46.37% <4.91%> (-0.07%) ⬇️
shard-2 34.54% <4.91%> (-0.05%) ⬇️
shard-3 32.04% <4.91%> (-0.05%) ⬇️
shard-4 32.11% <40.98%> (+<0.01%) ⬆️
shard-5 33.70% <4.91%> (-0.11%) ⬇️
shard-6 44.81% <4.91%> (-0.05%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
packages/gittensory-engine/src/ams-policy-spec.ts 40.98% <40.98%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

JSONbored added a commit that referenced this pull request Jul 12, 2026
Advances #5132

#5249 merged with AmsPolicySpec at 40.98% patch coverage: Codecov only
sees packages/gittensory-engine files through a root test/unit/*.test.ts
that actually imports and exercises them -- the engine package's own
node:test suite is a completely separate, Codecov-invisible
measurement. test/unit/miner-ams-policy.test.ts only exercised
resolveAmsPolicy's own call path, leaving most of parseAmsPolicySpec's
field-level branches (submissionMode/slopThreshold rejection,
capLimits/convergenceThresholds validation, malformed/oversized
content) uncovered from Codecov's perspective. Adds a root-level
mirror of the engine's own parser test, matching the established
miner-goal-spec-parser.test.ts pattern -- 100% statement/branch/line
coverage on ams-policy-spec.ts confirmed locally.
JSONbored added a commit that referenced this pull request Jul 12, 2026
#5253)

Advances #5132

#5249 merged with AmsPolicySpec at 40.98% patch coverage: Codecov only
sees packages/gittensory-engine files through a root test/unit/*.test.ts
that actually imports and exercises them -- the engine package's own
node:test suite is a completely separate, Codecov-invisible
measurement. test/unit/miner-ams-policy.test.ts only exercised
resolveAmsPolicy's own call path, leaving most of parseAmsPolicySpec's
field-level branches (submissionMode/slopThreshold rejection,
capLimits/convergenceThresholds validation, malformed/oversized
content) uncovered from Codecov's perspective. Adds a root-level
mirror of the engine's own parser test, matching the established
miner-goal-spec-parser.test.ts pattern -- 100% statement/branch/line
coverage on ams-policy-spec.ts confirmed locally.
JSONbored added a commit that referenced this pull request Jul 12, 2026
Closes #5132

The final assembly: attempt-cli.js now fetches a real SelfReviewContext
(#5145), builds a real coding-task spec (#5239, blocking on an
infeasible verdict), resolves the real AmsPolicySpec execution policy
(#5249, extended here with maxIterations/maxTurnsPerIteration -- the
same operator-risk-policy gap capLimits/submissionMode/slopThreshold
already covered), assembles the real IterateLoopInput + Governor
chokepoint context, and calls runMinerAttempt for real -- the first
point in this epic where a real coding agent actually executes rather
than every path ending in a reported block.

New attempt-input-builder.js holds the two pure composers
(buildAttemptGovernorContext, buildAttemptLoopInput) so the assembly
logic is independently unit-tested, mirroring coding-task-spec.js's
own precedent.

Real outcomes now surface distinct exit codes: 0 (submitted), 7
(abandon), 8 (stale), 9 (blocked by submission gate), 10 (governed),
plus the existing 4 (blocked_infeasible, repurposed from the old
blocked_missing_prerequisite placeholder), 5 (rejection_signaled), 6
(worktree_preparation_failed). The worktree is cleaned up on
submission, retained on any other real outcome for post-mortem
inspection (cleanupAttemptWorktree's own retention policy).

KNOWN, DOCUMENTED GAPS (not fabricated -- see attempt-input-builder.js
and attempt-cli.js's own header comments):
- governor.killSwitchRepoPaused only checks the GLOBAL env-var kill
  switch; a real per-repo .gittensory-miner.yml pause needs
  miner-goal-spec.js (#5255, open) wired in as a fast-follow.
- governor.convergenceInput is an honest first-attempt-shaped literal,
  not a real per-issue attempt-history query -- attempt-log.js's
  schema has no repo+issue index, and reenqueue counts aren't tracked
  anywhere yet (non-convergence.ts's own header: that belongs on the
  portfolio-queue table once it grows attempt-history columns, a real
  separate schema change).

This closes #5132 for the core attempt pipeline; #5135 (the autonomous
supervising loop) remains as separate, explicitly-flagged
safety-critical follow-up work.
JSONbored added a commit that referenced this pull request Jul 12, 2026
Closes #5132

The final assembly: attempt-cli.js now fetches a real SelfReviewContext
(#5145), builds a real coding-task spec (#5239, blocking on an
infeasible verdict), resolves the real AmsPolicySpec execution policy
(#5249, extended here with maxIterations/maxTurnsPerIteration -- the
same operator-risk-policy gap capLimits/submissionMode/slopThreshold
already covered), assembles the real IterateLoopInput + Governor
chokepoint context, and calls runMinerAttempt for real -- the first
point in this epic where a real coding agent actually executes rather
than every path ending in a reported block.

New attempt-input-builder.js holds the two pure composers
(buildAttemptGovernorContext, buildAttemptLoopInput) so the assembly
logic is independently unit-tested, mirroring coding-task-spec.js's
own precedent.

Real outcomes now surface distinct exit codes: 0 (submitted), 7
(abandon), 8 (stale), 9 (blocked by submission gate), 10 (governed),
plus the existing 4 (blocked_infeasible, repurposed from the old
blocked_missing_prerequisite placeholder), 5 (rejection_signaled), 6
(worktree_preparation_failed). The worktree is cleaned up on
submission, retained on any other real outcome for post-mortem
inspection (cleanupAttemptWorktree's own retention policy).

KNOWN, DOCUMENTED GAPS (not fabricated -- see attempt-input-builder.js
and attempt-cli.js's own header comments):
- governor.killSwitchRepoPaused only checks the GLOBAL env-var kill
  switch; a real per-repo .gittensory-miner.yml pause needs
  miner-goal-spec.js (#5255, open) wired in as a fast-follow.
- governor.convergenceInput is an honest first-attempt-shaped literal,
  not a real per-issue attempt-history query -- attempt-log.js's
  schema has no repo+issue index, and reenqueue counts aren't tracked
  anywhere yet (non-convergence.ts's own header: that belongs on the
  portfolio-queue table once it grows attempt-history columns, a real
  separate schema change).

This closes #5132 for the core attempt pipeline; #5135 (the autonomous
supervising loop) remains as separate, explicitly-flagged
safety-critical follow-up work.
JSONbored added a commit that referenced this pull request Jul 12, 2026
…5261)

* feat(miner): wire the real runMinerAttempt call into attempt-cli.js

Closes #5132

The final assembly: attempt-cli.js now fetches a real SelfReviewContext
(#5145), builds a real coding-task spec (#5239, blocking on an
infeasible verdict), resolves the real AmsPolicySpec execution policy
(#5249, extended here with maxIterations/maxTurnsPerIteration -- the
same operator-risk-policy gap capLimits/submissionMode/slopThreshold
already covered), assembles the real IterateLoopInput + Governor
chokepoint context, and calls runMinerAttempt for real -- the first
point in this epic where a real coding agent actually executes rather
than every path ending in a reported block.

New attempt-input-builder.js holds the two pure composers
(buildAttemptGovernorContext, buildAttemptLoopInput) so the assembly
logic is independently unit-tested, mirroring coding-task-spec.js's
own precedent.

Real outcomes now surface distinct exit codes: 0 (submitted), 7
(abandon), 8 (stale), 9 (blocked by submission gate), 10 (governed),
plus the existing 4 (blocked_infeasible, repurposed from the old
blocked_missing_prerequisite placeholder), 5 (rejection_signaled), 6
(worktree_preparation_failed). The worktree is cleaned up on
submission, retained on any other real outcome for post-mortem
inspection (cleanupAttemptWorktree's own retention policy).

KNOWN, DOCUMENTED GAPS (not fabricated -- see attempt-input-builder.js
and attempt-cli.js's own header comments):
- governor.killSwitchRepoPaused only checks the GLOBAL env-var kill
  switch; a real per-repo .gittensory-miner.yml pause needs
  miner-goal-spec.js (#5255, open) wired in as a fast-follow.
- governor.convergenceInput is an honest first-attempt-shaped literal,
  not a real per-issue attempt-history query -- attempt-log.js's
  schema has no repo+issue index, and reenqueue counts aren't tracked
  anywhere yet (non-convergence.ts's own header: that belongs on the
  portfolio-queue table once it grows attempt-history columns, a real
  separate schema change).

This closes #5132 for the core attempt pipeline; #5135 (the autonomous
supervising loop) remains as separate, explicitly-flagged
safety-critical follow-up work.

* fix(miner): use a real FocusManifest fixture in attempt-cli tests

CI's typecheck (not caught by an earlier stale local tsc run --
incremental cache silently no-op'd) correctly failed: the hand-rolled
{present, settings, gate, warnings} manifest fixture doesn't satisfy
FocusManifest's real shape (source, wantedPaths, preferredLabels,
linkedIssuePolicy, and 8 more required fields). Uses the engine's own
parseFocusManifest(undefined) to build a genuinely valid absent
manifest instead, and drops the `as never` cast in
miner-attempt-input-builder.test.ts that was silently working around
the same gap there.

* fix(miner): use a real RepositoryRecord shape in attempt-cli test fixtures

CI's typecheck (again not caught locally -- verified this time with a
genuinely fresh tsc run, all .tsbuildinfo caches cleared) correctly
failed: the hand-rolled repo fixture used the wrong field names
entirely (private/ownerLogin) instead of RepositoryRecord's real
shape (isPrivate/owner, plus required isInstalled/isRegistered).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant