v2.1.0 — M6 hardening & UX
M6 — hardening & UX. The post-v2.0.0 hardening milestone: the automation/completeness gaps
(G2–G4) and feature suggestions (F1–F4) surfaced by the v2.0.0 enterprise review, plus the deferred
cross-spec scope (#72). All opt-in and behind the unchanged pipeline — a MINOR release, no breaking
changes. Highlights: a thin executable phase orchestrator (eados.py), the /eados status doctor,
single-artifact render for refactor, an end-to-end phase smoke, risk-model weights as data,
auto-derived traceability links, hands-off Dependabot action-pin sync, and two new dogfooded gates
(version-lockstep, cross-cutting cross-spec-consistency).
Added
- M6 / 6.8 — cross-spec gate extended to cross-cutting gates (#72).
traceability-lint(the
git-spec CI gate, not a phase-transition gate) is now registered inworkflow.yaml's gate list
(cross-cutting,required_for: []), andeados_lint'scross-spec-consistencyvalidates
git.yaml'straceability.gateagainst that registry — so a typo'd cross-cutting gate id is
caught too (the scope deferred from #62).test_cross_spec.pycovers the resolve + typo cases.
Completes M6. No pipeline behavior change. - M6 / 6.7 — version-lockstep dogfooding (F4, #69). EADOS now dogfoods the
version-lockstep
gate it ships to generated repos: a neweados_lintcheck asserts every README release badge
(EN +docs/i18n/*) and the CHANGELOG's "the latest is vX.Y.Z" prose match the CHANGELOG's
latest released## [X.Y.Z]heading — so a release bump must move all of them in lockstep or the
self-lint fails. Pureversion_lockstep_problems(); covered by
tools/tests/test_version_lockstep.py. No pipeline behavior change. - M6 / 6.6 — auto-derive traceability links from PR bodies (F2, #67). A new
tools/derive_links.pybuilds the{pr, rfc, milestone, commit, release}traceability edges from
merged PRs —pr/commit/milestonefromghmetadata,rfcparsed from the body,release
from a release PR's title — and emits alinks.yamlthattraceability.py --linksconsumes, so
the graph runs on real data instead of a hand-maintained file. By default it emits only delivery
PRs (those with an rfc or milestone);--allemits every PR. The parser is pure and tested
gh-free; the optional fetch degrades cleanly (clear message, exit 2) whenghis absent,
unauthenticated, or offline — CI never depends on the network. Covered by
tools/tests/test_derive_links.py. No pipeline behavior change. - M6 / 6.5 — thin CLI phase orchestrator (G3, #64). A new
tools/eados.py <phase> <manifest>
runs a phase's deterministic outgoing gates — read fromworkflow.yaml(no hardcoded chain) —
evaluating the ones it can (manifest-valid,rfc-approved,roadmap-covers-rfcs) via the
sibling tools and marking render-time / human gates[manual], then prints the legal next
transitions and points at the procedure;eados.py statusdelegates to the doctor (6.4). It is
the executable spine beneath the markdown/eados <phase>procedures — it reports and gates,
never authoring or advancing state. Covered bytools/tests/test_eados.py. No pipeline behavior
change. - M6 / 6.4 —
/eados statusdoctor (F1, #66). A new read-onlytools/doctor.py(the
/eados statussurface,commands/status.md) reports a project's delivery health at a glance:
current phase (+ its owning role and what it produces), the legal next transitions (+ gates and
human-gating, viaphase_runner), the recordedrfcs/milestonesrefs, and traceability
coverage (roadmap-covers-rfcs, plustraceability-lintwhen a links file is present, via
traceability). It composes the existing tools — never re-implements — and exits non-zero on an
actionable problem (undeclared phase, uncovered RFC, dangling edge), doubling as a pre-flight
check. Covered bytools/tests/test_doctor.py. Read-only; no pipeline behavior change. - M6 / 6.3 — single-artifact render for the
refactorphase (G2, #63). A new
tools/render_artifact.pyrenders one template with the manifest context — reusing
render.py's engine and thevalidate_manifest+ unresolved-placeholder gates, so a single
artifact is byte-identical to its whole-render twin — and writes it into a target repo through
tools/sandbox.py(safe_write: contained, never.git, additive). It performs the "render the
missing artifact → sandbox" stepcommands/refactor.mddescribes (now invoked there instead of
done by hand). Covered bytools/tests/test_render_artifact.py. Factory tooling; no pipeline
behavior change. - CI — Dependabot pin-sync now auto-re-triggers via a GitHub App (ADR-0013 addendum). The
dependabot-pin-syncworkflow mints a short-lived App token (actions/create-github-app-token,
SHA-pinned) whenSYNC_APP_ID+SYNC_APP_PRIVATE_KEYare set and pushes the pin re-sync with it,
so CI re-triggers and the failedaction-pinscheck goes green by itself; absent the App
secrets it falls back toGITHUB_TOKEN(the fix still lands, check re-runs on the next event). The
setup guide is rewritten App-first;DEPENDABOT_SYNC_TOKENis now the swap-in PAT fallback, not the
default. Factory-only; no pipeline behavior change. - M6 / 6.1 — end-to-end phase-flow smoke (G4, #65). A new
tools/tests/test_phase_smoke.py
threads one coherent fixture project (manifest + RFC + ROADMAP + links) throughdesign → plan → auditby invoking the real phase tool CLIs (rfc_check,traceability,risk_score,
phase_runner) the way an agent runs a phase. It asserts each gate passes on the good
fixture and fails on a deliberately broken one, thatphase_runner --proposereports every
transition declared inworkflow.yamlLEGAL (and rejects an undeclared one), and that each entry
gate's backing tool exists on disk — catching tool-integration (seam) bugs the per-tool unit
tests cannot. Wired into the CI self-lint job. No pipeline behavior change. - M6 / 6.2 — risk-model weights as data (F3, #68). The factor weights (security surface, size,
blast radius), theblast_radius_threshold, and the points→level cutoffs move out of
risk_score.pyintorisk.yamlas data — each per-domain overridable (weights shallow-merged),
exactly asmandatory_gate_levelalready was (OQ2). The scorer reads them via a new
resolve(cfg, domain)with built-in fallbacks, so a pre-6.2risk.yamlstill scores identically
(back-compat) and the shipped default scores are unchanged.risk/_schema.mddocuments the new
keys; covered by the expandedtest_risk_score.py. Knowledge as data — no special-casing in code. - M6 / 6.9 — auto-sync shared action pins into the rendered templates (#76). A new
tools/sync_action_pins.py(--check/--fix) rewrites the workflow templates'
(templates/.github/workflows/*.tmpl) action pins to the factory CI's pin for each shared action
— reusingeados_lint's pin regex so the fixer and theaction-pinsgate can never disagree. A
weekly Dependabotgithub-actionsbump now needs no manual template edit to pass the lockstep
gate. The newdependabot-pin-syncworkflow (workflow_run, notpull_request_target;
ADR-0013) applies the fix automatically on a Dependabot PR — true zero-touch — gated to genuine
in-repo Dependabot PRs; the same--fixis the manual/local fallback (stay-current routine).
Deterministic and dependency-free; covered bytools/tests/test_sync_action_pins.py. Factory-only
(generated repos render no templates). No pipeline behavior change. - Docs —
DEPENDABOT_SYNC_TOKENsetup guide.maintenance/dependabot-sync-token.mddocuments
the optional token for green-by-itself Dependabot pin auto-sync (ADR-0013): fine-grained PAT
(drop-in) vs GitHub App (robust, with the workflow snippet), least-privilege (Contents-write only),
storage viagh secret set, verification, and rotation. Linked from the stay-current routine.
Maintainer-facing; no behavior change.