fix(ward-specs): stop shadowing ward's smart-defaults from the deployment bundle #472

Merged
coilyco-ops merged 4 commits from fix/defaults-are-wards-not-aos into main 2026-07-12 01:36:37 +00:00
Member

Third of the ward doctor failures blocking the dev-base image (aos#452). ward doctor against this branch goes from 2 failures to 1.

ward's defaults are THE defaults

loadBundleSmartDefaultsFrom starts from bakedSmartDefaults() and applies this block as an overlay, so a key omitted here keeps ward's product value. Every tuning key in this block just restated ward's own default - it bought nothing and rotted the moment ward moved.

One had already rotted:

FAIL smart defaults: agent-reservation-ttl 1h must exceed role "engineer"
                     execution-time-limit 90m (fail-closed)

agent-reservation-ttl "1h" here shadowed ward's "3h" and undercut the engineer role's 90m execution limit, so a reservation expired mid-run. doctor fails that invariant fail-closed, and doctor gates the core tier of the dev-base image - so this is one of the checks keeping publish-dev-base red and the published image stale at v0.245.0.

It was masked until now behind the burndown parse error that ward#1121 fixed.

agent-workflow stays: which repos are PR-gated is a coilyco deployment fact, not a ward product default. The test now pins that no ward tuning key comes back.

Verification

Inside agentic-os:latest:

  • uv run pytest - 455 passed
  • uv run pre-commit run --all-files - all hooks pass
  • ward doctor (ward main, this bundle): PASS smart defaults, PASS repo authority, PASS fleet, PASS exec bundle - 1 failure left

The one remaining failure needs a decision, and it looks like a real leak

FAIL ops bundle: guardfile base-url "git.example.com/api/v1" still looks like a placeholder

Two files in .ward/ both declare wrap ward-kdl ops forgejo:

  • guardfile.forgejo.kdl - forgejo.coilysiren.me/api/v1, /forgejo/coilyco-ops/api-token, restrict owner matches coily*
  • ops.forgejo.kdl - git.example.com/api/v1, /example/forgejo/api-token, restrict owner matches example*

ward merges both. I measured the effective result two ways and they disagree:

  • ward doctor reports the effective base-url as git.example.com
  • at runtime, ward ops forgejo get repo ... actually requests ssm get-parameter --name /forgejo/coilyco-ops/api-token - the real token

So the merged ops surface appears to pair the example host with the real credential. If that is what ships, ward sends Kai's live Forgejo token at git.example.com. doctor's placeholder check is the thing catching it, so it should not be silenced.

I did not touch this. Deciding whether ops.forgejo.kdl is a generic authoring source that should be excluded from the deployment bundle, or a deployment file that never got melded, is Kai's call - and it changes the live permission surface. Tracked in ward#1120.

Third of the `ward doctor` failures blocking the dev-base image (aos#452). **ward doctor against this branch goes from 2 failures to 1.** ## ward's defaults are THE defaults `loadBundleSmartDefaultsFrom` starts from `bakedSmartDefaults()` and applies this block as an **overlay**, so a key omitted here keeps ward's product value. Every tuning key in this block just restated ward's own default - it bought nothing and rotted the moment ward moved. One had already rotted: ``` FAIL smart defaults: agent-reservation-ttl 1h must exceed role "engineer" execution-time-limit 90m (fail-closed) ``` `agent-reservation-ttl "1h"` here shadowed ward's `"3h"` and undercut the engineer role's 90m execution limit, so **a reservation expired mid-run**. doctor fails that invariant fail-closed, and doctor gates the `core` tier of the dev-base image - so this is one of the checks keeping `publish-dev-base` red and the published image stale at v0.245.0. It was masked until now behind the `burndown` parse error that ward#1121 fixed. `agent-workflow` stays: which repos are PR-gated is a coilyco deployment fact, not a ward product default. The test now pins that no ward tuning key comes back. ## Verification Inside `agentic-os:latest`: * `uv run pytest` - **455 passed** * `uv run pre-commit run --all-files` - all hooks pass * `ward doctor` (ward main, this bundle): `PASS smart defaults`, `PASS repo authority`, `PASS fleet`, `PASS exec bundle` - **1 failure left** ## The one remaining failure needs a decision, and it looks like a real leak ``` FAIL ops bundle: guardfile base-url "git.example.com/api/v1" still looks like a placeholder ``` Two files in `.ward/` both declare `wrap ward-kdl ops forgejo`: * `guardfile.forgejo.kdl` - `forgejo.coilysiren.me/api/v1`, `/forgejo/coilyco-ops/api-token`, `restrict owner matches coily*` * `ops.forgejo.kdl` - `git.example.com/api/v1`, `/example/forgejo/api-token`, `restrict owner matches example*` ward merges both. I measured the **effective** result two ways and they disagree: * `ward doctor` reports the effective `base-url` as **`git.example.com`** * at runtime, `ward ops forgejo get repo ...` actually requests **`ssm get-parameter --name /forgejo/coilyco-ops/api-token`** - the **real** token So the merged ops surface appears to pair the **example host** with the **real credential**. If that is what ships, ward sends Kai's live Forgejo token at `git.example.com`. doctor's placeholder check is the thing catching it, so it should **not** be silenced. I did not touch this. Deciding whether `ops.forgejo.kdl` is a generic authoring source that should be excluded from the deployment bundle, or a deployment file that never got melded, is Kai's call - and it changes the live permission surface. Tracked in ward#1120.
fix(ward-specs): stop shadowing ward smart-defaults from the deployment bundle
All checks were successful
ci / gate (pull_request) Successful in 11s
446aae4d49
ward embeds the smart-defaults, and this bundle only overlays them:
loadBundleSmartDefaultsFrom starts from bakedSmartDefaults, so a key omitted
here keeps ward product value. Every tuning key in this block just restated
ward own default, so it bought nothing and rotted the moment ward moved.

One had already rotted. agent-reservation-ttl "1h" shadowed ward "3h" and
undercut the engineer role 90m execution-time-limit, so a reservation expired
mid-run. ward doctor fails that invariant fail-closed:

    FAIL smart defaults: agent-reservation-ttl 1h must exceed role "engineer"
                         execution-time-limit 90m (fail-closed)

doctor gates the core tier of the dev-base image, so this is one of the checks
keeping publish-dev-base red and the published image stale (aos#452). It was
masked until now behind the burndown parse error that ward#1121 fixed.

agent-workflow stays: which repos are PR-gated is a coilyco deployment fact,
not a ward product default.

Refs aos#452, ward#1120.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(ward-specs): mark the generic ops singletons as (placeholder) sentinels
All checks were successful
ci / gate (pull_request) Successful in 11s
99263e6862
base-url, auth, and the owner gate in ops.forgejo.kdl are sentinels, not
config: ward must never embed real deployment values, so the generic surface
has to name some host, token, and owner gate.

Unmarked they were indistinguishable from config, and cli-guard singletons are
last-wins, so the sentinel base-url beat the real one from guardfile.forgejo.kdl
while the auth lost. The merged ops surface paired git.example.com with the live
/forgejo/coilyco-ops/api-token. A sentinel that can win a merge is worse than no
sentinel at all.

(placeholder) makes them yield to whatever the selected bundle supplies, whatever
the merge order, and keeps ward doctor failing a deployment that supplied nothing.

Refs ward#1122, aos#452.
fix(ward-specs): regenerate the forgejo spec lock so it carries the commit-status ops
All checks were successful
ci / gate (pull_request) Successful in 10s
1e239ed86a
agentic-os#446 added the commit-status verbs (repoGetCombinedStatusByRef,
repoListStatusesByRef) to guardfile.forgejo.kdl, but the spec lock was never
regenerated. It carried 47 operationIds and zero status ops, so specverb failed
the merged ops surface fail-closed:

    FAIL ops bundle: specverb: operationId repoGetCombinedStatusByRef not found
                     in spec (fail-closed)

That failure was invisible until now: the placeholder base-url check
short-circuited validateGuardfileOperational before buildForgejoOps ever ran, so
the stale lock never got a chance to fail. ward#1123 clears the placeholder and
uncovers this.

Regenerated with the kdl-specs driver (lock --guardfile), which refetches the
upstream spec and prunes it to the verbs the guardfile declares: 52 ops now,
including the status pair.

Refs aos#452, ward#1123.
fix(ward-specs): drop the generic ops guardfile copy and lock the real surface
All checks were successful
ci / gate (pull_request) Successful in 7s
44e30b43f9
ops.forgejo.kdl was a generic copy of the same wrap ward-kdl ops forgejo block
that guardfile.forgejo.kdl already provides, sitting in the deployment bundle
ward fetches through WARD_CONFIG_REF. ward merged both, and because cli-guard
singletons are last-wins the generic copy example base-url beat the real one
while the deployment auth still won: the merged ops surface pointed at
git.example.com carrying the live /forgejo/coilyco-ops/api-token.

It is not lockable as a deployment surface either. The kdl-specs driver fetches
each guardfile spec from its own base-url, so locking it tries git.example.com
and fails DNS. It only ever made sense as ward generic authoring source, which
lives in ward tree, not in Kai coilyco bundle.

guardfile.forgejo.kdl is the real surface: forgejo.coilysiren.me, the coilyco-ops
token, the coily* owner gate, and 54 verbs. The manifest now names it, and the
spec lock is regenerated against it so specverb resolves every op it declares.

With this, ward doctor passes every check against the bundle for the first time,
which unblocks the core tier of the dev-base image (aos#452). WARD_VERSION moves
0.567.0 -> 0.628.0, the first release carrying the cli-guard unstranding and the
burndown block (ward#1121).

Refs aos#452, ward#1122, ward#1121.
Sign in to join this conversation.
No description provided.