Skip to content

feat(v0.7.0): Docker workflow enforcement + tooling preflight - #66

Merged
tonmoy007 merged 7 commits into
developfrom
feat/v0.7.0-docker-tooling
Aug 10, 2026
Merged

feat(v0.7.0): Docker workflow enforcement + tooling preflight#66
tonmoy007 merged 7 commits into
developfrom
feat/v0.7.0-docker-tooling

Conversation

@tonmoy007

Copy link
Copy Markdown
Owner

Summary

Implements v0.7.0 (T-227..T-233) from build/01-srs/srs-v0.7.0.md -- a fail-soft,
never-block Docker-and-tooling layer, orthogonal to the Builder Pro work already on
develop (no shared files). See ADR-012 for the full design.

  • T-227: references/tool-registry.md (declarative, seeded docker/docker compose/gh) + scripts/tool_preflight.py (detection + .forge/tool-status.json
    cache, mirrors the T-138 capability-probe pattern).
  • T-228: scripts/check_docker_readiness.py -- advisory hygiene check (pinned
    base image, HEALTHCHECK, non-root USER, .dockerignore, compose validity). Always
    exits 0, even with findings.
  • T-229: doctor.py gains check_required_tools (one warn per missing required
    tool).
  • T-230: session-start.py tool advisory -- budget-aware, dropped first
    under token pressure, ahead of lessons/rules.
  • T-231: /forge:preflight -- the sole surface that runs an installer, only
    after per-tool user confirmation.
  • T-232: opt-in docker profile (never auto-assigned over a real app type) +
    unconditional cross-cutting hygiene wiring into /forge:deploy +
    detect-project-type.py has_docker/docker_indicators/suggestion.
  • T-233: ADR-012, references/docker-and-tooling.md, README/ROADMAP/progress/
    decisions docs.

Not tagged as v0.7.0 separately -- per user direction, this folds into the next
release, v0.8.0, alongside the already-merged Builder Pro work (same precedent
as v0.3.2 folding into v0.3.3). ROADMAP.md reflects this.

Test plan

  • TDD red-first throughout: every task's tests confirmed failing before its
    implementation existed
  • Full unit suite: 2017 passed (baseline 1949 -> +68)
  • scripts/validate-plugin.py: exit 0
  • tests/integration/full-pipeline.sh: 12/12 gates + traceability intact

🤖 Generated with Claude Code

Adds references/tool-registry.md (declarative markdown-with-YAML, same
shape as gate-criteria.md/project-type-profiles.md) seeded with docker,
docker compose, and gh -- name/which/version_probe/workflows/stages/
required_when/per-OS install per entry. A new tool is a data entry,
not code.

scripts/tool_preflight.py: pure detection (shutil.which + optional
version_probe -- a present binary whose specific capability probe
fails, e.g. docker without the compose plugin, counts as absent),
cached to .forge/tool-status.json with a 24h TTL, mirroring
hooks/_background_agent.py's capability-probe pattern (T-138).
required_when resolves against project state (Docker artifacts present
via a tree walk / current_stage == 12 via pipeline/state.md). Never
raises: malformed registry entries are skipped not fatal, an
unreadable registry/cache degrades to empty/no-op, _state_lib's
SystemExit-on-missing-state.md is caught explicitly. install_command()
only ever returns a string -- verified by a test that fails the suite
if subprocess.run is ever called.

TDD red-first: 21 tests confirmed failing on ModuleNotFoundError before
implementation, all green after. Full unit suite 1970 pass, zero
regressions.

Ref: T-227
Mirrors check_store_readiness.py's no-op-when-absent template, but
purely advisory: exits 0 whether or not Docker artifacts are found,
and exits 0 even when it reports findings (WARN: lines) -- never a
blocking gate.

Per Dockerfile: base image pinned (digest pin or a reference to an
earlier build stage both count as pinned, only an unpinned/`:latest`
registry image flags), HEALTHCHECK present, last USER directive isn't
root. Project-level: .dockerignore exists, each compose file parses
and has a top-level services: key (fail-soft without PyYAML -- skips,
doesn't crash). Unreadable files are skipped, not fatal.

TDD red-first: 13 tests confirmed failing on ModuleNotFoundError,
all green after implementation. Full unit suite 1983 pass, zero
regressions.

Ref: T-228
Adds check_required_tools(forge_root, cwd) -> list[CheckResult]: one
warn-level result per missing required tool_preflight tool, with the
resolved install command as the fix -- a present or not-required tool
produces no result. Wired into run_checks(). Doctor stays read-only:
it surfaces the install command, it never runs it. Never raises --
a tool_preflight error degrades to no results, same posture as every
other doctor check.

+5 tests. Full unit suite 1988 pass, zero regressions.

Ref: T-229
Adds _ensure_tool_status() (mirrors _ensure_capabilities: detached
`tool_preflight.py refresh` subprocess, fired only when
.forge/tool-status.json is missing or older than the 24h TTL, never
raises) and _tool_preflight_block() (one advisory line per missing
required tool, read from the cached JSON with stdlib only -- '' when
nothing missing, no cache, or an unreadable cache).

Restructures the budget cascade so the tool block is dropped FIRST
under token pressure, before lessons trim and the rules drop
(REQ-TR-004, AC-TR-003) -- _compose() gained a tool_text parameter
threaded through the same three-step fallback that already existed for
lessons/rules.

+10 tests. Full unit suite 1998 pass, zero regressions.

Ref: T-230
New skills/forge-preflight/SKILL.md (name: preflight, allowed-tools:
[Read, Bash]): runs tool_preflight.py check, presents each missing
required tool with its per-OS install command (sourced from the
registry, never invented), and runs a command via Bash only after the
user explicitly confirms that specific tool. Declined tools are never
installed. The single "offer to install" surface in Forge --
session-start and doctor.py only ever detect and report.

TDD red-first: 6 structural tests confirmed failing (file missing)
before the skill existed, all green after -- frontmatter parses
(avoids the 2026-06-22 ': ' YAML trap), allowed-tools correct,
documents detect->confirm->install, states it never auto-runs.

Full unit suite 2004 pass, zero regressions.

Ref: T-231
references/project-type-profiles.md: new `## Profile: docker` block --
stage_emphasis high on architecture/deploy; stage_3 additional_concerns
(multi-stage builds, base-image pinning, build secrets/volumes, layer
caching); stage_8 additional_steps (build/scan/push) +
additional_criteria G8-DOCKER-001 -> check_docker_readiness.py
(severity: warning); stage_9 additional_concerns (container restart/
OOM-kill monitoring, resource alerting) added to satisfy the T-024
>=3-stage-override invariant so docker joins the "all standard
profiles parse" parity test. Opt-in and never a precondition for the
cross-cutting Docker handling that already runs unconditionally
(T-228/forge-deploy).

skills/forge-deploy/SKILL.md: runs check_docker_readiness.py --cwd .
unconditionally in pre-flight (every project, regardless of profile,
self-no-ops without Docker artifacts) and relays WARN: findings as
advisory -- never blocking, never gating the stage.

scripts/detect-project-type.py: _finalize() now emits has_docker +
docker_indicators (cross-cutting, applied regardless of `type`, a
FastAPI-in-Docker project stays `api`) and adds
suggested_profile: "docker" only when type is unknown and Docker
artifacts are present with nothing else matched (mirrors the existing
script-suggestion pattern, never auto-assigned over a real app type).

set-profile.py and load-profile.py needed no code changes -- both
already discover profiles dynamically from project-type-profiles.md.

+23 tests across 5 files (detect-project-type, set-profile,
load-profile parity, deploy-skill structural). Full unit suite 2017
pass, validate-plugin.py exit 0, zero regressions.

Ref: T-232
ADR-012 (build/02-architecture/adr/012-docker-and-tooling.md): records
the cross-cutting-handling + opt-in-profile architecture, advisory
never-block hygiene, detect-in-hook/install-in-skill split, and the
declarative registry decision, with alternatives considered and
consequences.

references/docker-and-tooling.md: user-facing reference for the
registry format, detection/cache, surfacing, /forge:preflight, hygiene
checks, and the docker profile.

README.md: new capability bullet + /forge:preflight command row.
ROADMAP.md: v0.7.0 marked `~~v0.7.0~~ | folded` -- built as T-227-233
but folded into the v0.8.0 release alongside Builder Pro rather than
tagged separately (same precedent as v0.3.2 folding into v0.3.3), per
explicit user direction.

progress.md: v0.7.0 COMPLETE row (T-227..T-233), full test/commit
trail. decisions.md: new entry recording the architecture decision and
the fold-into-v0.8.0 release decision.

Full unit suite 2017 pass, validate-plugin.py exit 0, full-pipeline.sh
12/12 gates + traceability intact.

Ref: T-233
@tonmoy007
tonmoy007 merged commit b9e98ea into develop Aug 10, 2026
3 checks passed
@tonmoy007
tonmoy007 deleted the feat/v0.7.0-docker-tooling branch August 10, 2026 21:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants