Context
#6060's decided ADR (see that issue's closing comment for the full decision) adds a new .loopover-ams.yml/AmsPolicySpec field, selfLoopAutonomy: AutonomyLevel, gating the iterate-loop's self-directed pass->handoff transition. This issue is the config-schema half only -- get the field parsed, defaulted, and warned-on with zero behavior change to decideNextAction itself. A separate, blocked-on-this issue consumes the field.
AmsPolicySpec (packages/loopover-engine/src/ams-policy-spec.ts:40-54) is the OPERATOR's own local execution-risk policy type for .loopover-ams.yml -- deliberately distinct from .loopover-miner.yml/MinerGoalSpec (packages/loopover-engine/src/miner-goal-spec.ts:9-14), which is the TARGET REPO's own checked-in preferences file. The type's own header (ams-policy-spec.ts:9-13) explains why an autonomy dial belongs here and not there: a target repo's own checked-in file must never get to raise the operator's own risk tolerance against that exact repo. selfLoopAutonomy is exactly that kind of risk-tolerance knob, so it sits alongside submissionMode/capLimits on AmsPolicySpec, resolved operator-locally per ams-policy-spec.ts:15-18.
The AutonomyLevel type ("observe" | "auto_with_approval" | "auto") already exists at packages/loopover-engine/src/types/manifest-deps-types.ts:138, and the level vocabulary is already enumerated as AUTONOMY_LEVELS at packages/loopover-engine/src/settings/autonomy.ts:7. Neither is currently re-exported from the engine's public barrel (packages/loopover-engine/src/index.ts) -- but every OTHER field-specific type on AmsPolicySpec (AmsSubmissionMode, AmsSlopThreshold, AmsCapLimits) IS exported alongside AmsPolicySpec itself in the barrel's existing export block (index.ts:519-529), so AutonomyLevel needs the same treatment for surface consistency.
Requirements
- Default MUST be
"auto", not "observe". This is a deliberate, required deviation from settings/autonomy.ts's own DEFAULT_AUTONOMY_LEVEL = "observe" (settings/autonomy.ts:18). That default is correct for the maintainer auto-maintain dial because it gates a capability with zero prior acting behavior. This field gates behavior that ALREADY happens unconditionally today (a clean self-review pass -> "handoff", iterate-policy.ts:154-156): defaulting to "observe" here would silently change existing behavior for every repo that leaves the field unset. Do not copy settings/autonomy.ts's default.
- Add
selfLoopAutonomy: AutonomyLevel to the AmsPolicySpec type (ams-policy-spec.ts:40-54), importing AutonomyLevel the same way settings/autonomy.ts:1 does: import type { AutonomyLevel } from "./types/manifest-deps-types.js";.
- Add
selfLoopAutonomy: "auto" to DEFAULT_AMS_POLICY_SPEC (ams-policy-spec.ts:67-74) and to cloneDefaultAmsPolicySpec (ams-policy-spec.ts:78-87).
- Add a
normalizeSelfLoopAutonomy(value, fallback, warnings) validator mirroring normalizeSubmissionMode's exact shape (ams-policy-spec.ts:93-98), but validating against the three AUTONOMY_LEVELS values instead of AmsSubmissionMode's two.
- Wire it into
hasConfiguredPolicyFields (ams-policy-spec.ts:159-171: add a spec.selfLoopAutonomy !== DEFAULT_AMS_POLICY_SPEC.selfLoopAutonomy clause) and into parseAmsPolicySpec's field assignment (ams-policy-spec.ts:196-212), same pattern as every existing field.
- Export
type AutonomyLevel from the engine barrel's existing ams-policy-spec.js export block in index.ts:519-529, alongside AmsCapLimits/AmsPolicySpec/etc., for surface consistency with every other AmsPolicySpec field type.
- No behavior change to
decideNextAction/iterate-policy.ts in this issue -- pure config-surface addition. The field is inert until the follow-up consultation issue consumes it.
Deliverables
Test Coverage Requirements
Read this before starting. packages/loopover-engine/src/** is measured by Codecov's 99% patch gate ONLY via the ROOT vitest suite (test/**/*.test.ts, per vitest.config.ts's coverage.include and test.include). packages/loopover-engine's OWN test/*.test.ts files run via node --test (its package.json's test script) and are entirely invisible to Codecov. A PR that only adds tests under packages/loopover-engine/test/ will show near-0% patch coverage on this diff and get auto-closed. Update BOTH suites, for different reasons:
test/unit/ams-policy-spec-parser.test.ts (root, vitest, imports from ../../packages/loopover-engine/src/index per that file's own existing pattern) -- REQUIRED for Codecov patch coverage.
packages/loopover-engine/test/ams-policy-spec-parser.test.ts (package-local, node --test, imports from ../dist/index.js) -- REQUIRED to keep npm run test --workspace @loopover/engine (part of test:ci) green, since it exercises the built output.
- Cover every new branch: default-omitted (
"auto"), each of the 3 valid values accepted with present: true, an invalid string value (falls back to "auto" with a warning mentioning selfLoopAutonomy and the three allowed values), and a non-string value.
- A regression test asserting
parseAmsPolicySpec({}) (or any input with zero configured fields) still returns present: false and spec deep-equal to the full, unmodified DEFAULT_AMS_POLICY_SPEC -- proving hasConfiguredPolicyFields's new clause doesn't false-positive on the default value.
Expected Outcome
.loopover-ams.yml accepts selfLoopAutonomy: observe|auto_with_approval|auto, tolerantly parsed and warned-on like every other AmsPolicySpec field, defaulting to "auto" (today's implicit behavior) when unset. decideNextAction is not yet touched -- a separate issue consumes this field.
Links & Resources
Context
#6060's decided ADR (see that issue's closing comment for the full decision) adds a new.loopover-ams.yml/AmsPolicySpecfield,selfLoopAutonomy: AutonomyLevel, gating the iterate-loop's self-directed pass->handoff transition. This issue is the config-schema half only -- get the field parsed, defaulted, and warned-on with zero behavior change todecideNextActionitself. A separate, blocked-on-this issue consumes the field.AmsPolicySpec(packages/loopover-engine/src/ams-policy-spec.ts:40-54) is the OPERATOR's own local execution-risk policy type for.loopover-ams.yml-- deliberately distinct from.loopover-miner.yml/MinerGoalSpec(packages/loopover-engine/src/miner-goal-spec.ts:9-14), which is the TARGET REPO's own checked-in preferences file. The type's own header (ams-policy-spec.ts:9-13) explains why an autonomy dial belongs here and not there: a target repo's own checked-in file must never get to raise the operator's own risk tolerance against that exact repo.selfLoopAutonomyis exactly that kind of risk-tolerance knob, so it sits alongsidesubmissionMode/capLimitsonAmsPolicySpec, resolved operator-locally perams-policy-spec.ts:15-18.The
AutonomyLeveltype ("observe" | "auto_with_approval" | "auto") already exists atpackages/loopover-engine/src/types/manifest-deps-types.ts:138, and the level vocabulary is already enumerated asAUTONOMY_LEVELSatpackages/loopover-engine/src/settings/autonomy.ts:7. Neither is currently re-exported from the engine's public barrel (packages/loopover-engine/src/index.ts) -- but every OTHER field-specific type onAmsPolicySpec(AmsSubmissionMode,AmsSlopThreshold,AmsCapLimits) IS exported alongsideAmsPolicySpecitself in the barrel's existing export block (index.ts:519-529), soAutonomyLevelneeds the same treatment for surface consistency.Requirements
"auto", not"observe". This is a deliberate, required deviation fromsettings/autonomy.ts's ownDEFAULT_AUTONOMY_LEVEL = "observe"(settings/autonomy.ts:18). That default is correct for the maintainer auto-maintain dial because it gates a capability with zero prior acting behavior. This field gates behavior that ALREADY happens unconditionally today (a clean self-review pass ->"handoff",iterate-policy.ts:154-156): defaulting to"observe"here would silently change existing behavior for every repo that leaves the field unset. Do not copysettings/autonomy.ts's default.selfLoopAutonomy: AutonomyLevelto theAmsPolicySpectype (ams-policy-spec.ts:40-54), importingAutonomyLevelthe same waysettings/autonomy.ts:1does:import type { AutonomyLevel } from "./types/manifest-deps-types.js";.selfLoopAutonomy: "auto"toDEFAULT_AMS_POLICY_SPEC(ams-policy-spec.ts:67-74) and tocloneDefaultAmsPolicySpec(ams-policy-spec.ts:78-87).normalizeSelfLoopAutonomy(value, fallback, warnings)validator mirroringnormalizeSubmissionMode's exact shape (ams-policy-spec.ts:93-98), but validating against the threeAUTONOMY_LEVELSvalues instead ofAmsSubmissionMode's two.hasConfiguredPolicyFields(ams-policy-spec.ts:159-171: add aspec.selfLoopAutonomy !== DEFAULT_AMS_POLICY_SPEC.selfLoopAutonomyclause) and intoparseAmsPolicySpec's field assignment (ams-policy-spec.ts:196-212), same pattern as every existing field.type AutonomyLevelfrom the engine barrel's existingams-policy-spec.jsexport block inindex.ts:519-529, alongsideAmsCapLimits/AmsPolicySpec/etc., for surface consistency with every otherAmsPolicySpecfield type.decideNextAction/iterate-policy.tsin this issue -- pure config-surface addition. The field is inert until the follow-up consultation issue consumes it.Deliverables
selfLoopAutonomy: AutonomyLevelonAmsPolicySpec, default"auto", threaded throughDEFAULT_AMS_POLICY_SPEC/cloneDefaultAmsPolicySpec/hasConfiguredPolicyFields/parseAmsPolicySpec.normalizeSelfLoopAutonomyvalidator, warning on any value outsideAUTONOMY_LEVELS.type AutonomyLevelexported from the engine barrel.Test Coverage Requirements
"auto"), each of the 3 valid values accepted withpresent: true, an invalid string value (falls back to"auto"with a warning mentioningselfLoopAutonomyand the three allowed values), and a non-string value.parseAmsPolicySpec({})(or any input with zero configured fields) still returnspresent: falseandspecdeep-equal to the full, unmodifiedDEFAULT_AMS_POLICY_SPEC-- provinghasConfiguredPolicyFields's new clause doesn't false-positive on the default value.Expected Outcome
.loopover-ams.ymlacceptsselfLoopAutonomy: observe|auto_with_approval|auto, tolerantly parsed and warned-on like every otherAmsPolicySpecfield, defaulting to"auto"(today's implicit behavior) when unset.decideNextActionis not yet touched -- a separate issue consumes this field.Links & Resources
packages/loopover-engine/src/ams-policy-spec.tspackages/loopover-engine/src/settings/autonomy.ts(AUTONOMY_LEVELS,DEFAULT_AUTONOMY_LEVEL)packages/loopover-engine/src/types/manifest-deps-types.ts:138(AutonomyLevel)decideNextActionconsultation issue (blocked by this one, filed as a sibling sub-issue of Decide AMS's own autonomy-level dial for iterate-policy.ts's decideNextAction #6060)