Skip to content

feat(env)!: adopt yaml env overlays and injected session flows#33

Merged
roodboi merged 25 commits into
mainfrom
feat/pulumi-style-env-config
Mar 27, 2026
Merged

feat(env)!: adopt yaml env overlays and injected session flows#33
roodboi merged 25 commits into
mainfrom
feat/pulumi-style-env-config

Conversation

@roodboi

@roodboi roodboi commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • replace the legacy project env model with committed hack.env.default.yaml and optional hack.env.<overlay>.yaml
  • switch runtime behavior to direct env injection by default, with manual hack env materialize for compatibility only
  • add hack env add, hack env exec, hack env shell, env-aware session flows, and CI fallback via HACK_ENV_SECRET_KEY
  • update docs, agent templates, MCP docs, and skills to reflect the new workflow

Breaking change

This PR changes the canonical project env format.

Repos should migrate from legacy .hack/hack.env.json and backend-first env storage to the new YAML overlay model:

  • hack.env.default.yaml
  • optional hack.env.<overlay>.yaml
  • local .hack.secret.key or HACK_ENV_SECRET_KEY

Use:

hack doctor --migrate-env-config

Verification

  • bun x ultracite check src/mcp/agent-docs.ts src/commands/env.ts src/commands/session.ts src/lib/hack-env.ts src/lib/project-env-config.ts src/mux/mux-backend.ts src/mux/tmux-backend.ts src/mux/zellij-backend.ts tests/session-help.test.ts tests/env-exec-command.test.ts tests/session-env-command.test.ts
  • git diff --check

roodboi added 6 commits March 27, 2026 12:35
Replace the legacy .hack/hack.env.json and backend-first project env model with committed hack.env.*.yaml overlays, project-key encrypted secrets, direct runtime injection, host-side env exec/shell helpers, env-aware sessions, and updated docs/templates.

BREAKING CHANGE: project env configuration now uses hack.env.default.yaml and optional hack.env.<overlay>.yaml files as the canonical format. .hack/hack.env.json, backend-first secret flows, and .hack/.env as the default runtime source are legacy compatibility paths. Migrate older repos with hack doctor --migrate-env-config and use hack env add/materialize/exec/shell for the new workflow.
@changeset-bot

changeset-bot Bot commented Mar 27, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: aee89ba

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d93363408d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/project-env-config.ts Outdated
Comment thread src/lib/project-env-config.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 066c4cbb0e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/commands/env.ts Outdated
Comment thread src/commands/session.ts Outdated
paulkarcher
paulkarcher previously approved these changes Mar 27, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 32dcea0e74

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/commands/doctor.ts
Comment on lines +1975 to +1978
const cleanupCandidates = [
...migrated.cleanupCandidates,
...blockedCleanupCandidates,
].sort((left, right) => left.localeCompare(right));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude compose-blocked env files from cleanup removal

This cleanup list currently appends blockedCleanupCandidates even when compose env_file references were not repaired, so accepting the cleanup prompt can delete .hack/.env* files that compose still depends on. In the common path where the user declines the compose repair step, this turns a successful migration into a runtime break (compose will still reference files that were removed). Keep blocked candidates out of the removable set until the compose references are actually cleared.

Useful? React with 👍 / 👎.

Comment thread src/commands/session.ts
Comment on lines +1263 to +1266
const injectedEnv =
workspaceProject && projectName
? await resolveSessionInjectedEnv({
project: workspaceProject,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Inject env in session exec only when scope flags are set

hack session exec now resolves and injects env whenever the workspace can be mapped to a project, even if the user did not pass --env/--service. That means commands sent to an already scoped workspace (for example ...env-qa.svc-api) can be overridden by the current default overlay/global scope, which is a silent context switch for migrations or admin commands. Injection should be conditional on explicit scope flags (or inferred from workspace scope) instead of always applying defaults.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1954903dd3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/project-meta.ts Outdated
Comment on lines +339 to +343
const resolved = await resolveProjectEnvConfig({
projectRoot: opts.projectRoot,
projectDir: opts.projectDir,
envName: undefined,
serviceNames,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle malformed env YAML without aborting project metadata

resolveModernEnvMeta calls resolveProjectEnvConfig directly, and parse errors from .hack/hack.env*.yaml throw here; that rejection bubbles through resolveProjectMeta and buildMetaByProjectName (Promise.all), so a single malformed project env file can make hack projects metadata fail entirely instead of reporting an env parse error for that project. The legacy path preserved this as contractParseError, so this should catch and downgrade parse failures to metadata fields rather than throwing.

Useful? React with 👍 / 👎.

Comment thread src/commands/session.ts Outdated
Comment on lines +417 to +421
? await resolveSessionInjectedEnv({
project: projectContext,
projectName: baseName,
envName: undefined,
serviceName: null,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve scoped env when cloning an attached workspace

In the interactive picker path for an attached workspace, selecting “Create isolated” keeps the scoped workspace name (resolveNextIsolatedWorkspaceName on the original scoped name) but injects env using envName: undefined and serviceName: null, so the new workspace can run with default/global env while its name still advertises a scoped env/service. This mismatch can silently run commands against the wrong environment when users branch from a scoped workspace.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2c6b5dc53e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/templates.ts Outdated
`environment: ${environment}`,
"secretsprovider: project_key",
"values:",
" global:",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Emit an object for default global scope in env template

renderProjectEnvConfigYaml() writes values.global as global: with no mapping, which YAML parses as null. New projects created via hack init then produce an invalid hack.env.default.yaml, and subsequent modern env operations (for example hack env add ...) fail parse validation (Env scope "global" must be an object). This blocks the primary env workflow immediately after initialization.

Useful? React with 👍 / 👎.

Comment thread src/commands/session.ts Outdated
Comment on lines +1343 to +1344
(args.options.env !== undefined || serviceName !== null) &&
!(workspaceProject && projectName)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat explicit --service global as env injection request

In session exec, the unresolved-project guard checks serviceName !== null, but resolveRequestedServiceName() normalizes --service global to null. If a workspace cannot be mapped to a project, hack session exec --service global ... bypasses the error path and still runs the command without any injected env, silently ignoring an explicit scope flag. This should key off args.options.service !== undefined (like --env) rather than the normalized value.

Useful? React with 👍 / 👎.

@roodboi
roodboi merged commit b064522 into main Mar 27, 2026
3 checks passed
@roodboi
roodboi deleted the feat/pulumi-style-env-config branch March 27, 2026 21:29

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dd649b7cd3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/commands/session.ts
Comment on lines +1342 to +1344
if (
(args.options.env !== undefined || serviceName !== null) &&
!(workspaceProject && projectName)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject scoped exec when workspace project cannot be resolved

When hack session exec is run against a scoped workspace name (for example project.env-qa.svc-api) but the project context cannot be resolved (stale registry entry, running outside repo, etc.), this guard does not fire because it only checks explicit flags. The command then proceeds with env: undefined, silently dropping the inferred scoped env injection and running with ambient host env instead. For migration/admin commands this can target the wrong environment without an error; the unresolved-project check should also trigger when inferred scope selection requires injection.

Useful? React with 👍 / 👎.

roodboi pushed a commit that referenced this pull request Mar 27, 2026
## 2.0.0 (2026-03-27)

* fix(build): stabilize macOS release signing ([32dcea0](32dcea0))
* fix(ci): stabilize env migration branch checks ([498d793](498d793))
* fix(doctor): group checks and hide noisy follow-up ([e6f6ace](e6f6ace))
* fix(doctor): make env migration prompts explicit ([b000438](b000438))
* fix(doctor): repair compose env refs in modern projects ([1954903](1954903))
* fix(doctor): repair legacy env cleanup and tickets retry ([1685328](1685328))
* fix(env): clean legacy config during migration ([eac34d6](eac34d6))
* fix(env): keep app and project metadata compatible ([7d6fe17](7d6fe17))
* fix(env): keep init templates and session scope strict ([aee89ba](aee89ba))
* fix(env): store canonical env configs under .hack ([f4ff398](f4ff398))
* fix(env): support CI key fallback and mask modern secrets ([85e88f8](85e88f8))
* fix(env): tighten scope validation for modern overlays ([27dd7c1](27dd7c1))
* fix(runtime): degrade gracefully when docker is unavailable ([ad0d2f9](ad0d2f9))
* fix(session): preserve scoped env injection semantics ([64e46e1](64e46e1))
* fix(test): account for runtime-unavailable offline status ([0a0e5ab](0a0e5ab))
* fix(test): isolate offline optionality matrix ([c93ae8a](c93ae8a))
* fix(test): isolate session env suite from Bun mock bleed ([f7c9a2d](f7c9a2d))
* fix(tickets): force fresh remote checkout during repair ([a58cff4](a58cff4))
* fix(tickets): isolate hidden ref fetches from remote refmap ([dd649b7](dd649b7))
* fix(tickets): repair against refreshed hidden refs ([c1c2498](c1c2498))
* fix(tickets): retry non-fast-forward repair pushes ([2c6b5dc](2c6b5dc))
* feat(env)!: adopt project env config overlays ([a986dfd](a986dfd))
* feat(env)!: adopt yaml env overlays and injected session flows ([d933634](d933634))
* Merge branch 'main' into feat/pulumi-style-env-config ([066c4cb](066c4cb))
* Merge pull request #33 from hack-dance/feat/pulumi-style-env-config ([b064522](b064522)), closes [#33](#33)
* feat(env): redesign portability around Pulumi-style config ([02228a2](02228a2))
* feat(env): refine interactive config and key bootstrapping ([bd99f6e](bd99f6e))

### BREAKING CHANGE

* project env configuration now uses hack.env.default.yaml and optional hack.env.<overlay>.yaml files as the canonical format. .hack/hack.env.json, backend-first secret flows, and .hack/.env as the default runtime source are legacy compatibility paths. Migrate older repos with hack doctor --migrate-env-config and use hack env add/materialize/exec/shell for the new workflow.
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