Skip to content

Commit ab2e5ec

Browse files
alicodingclaude
andauthored
feat: the shell step can run inside a Configure-authored environment (goal 0240 S4) (#520)
process-shell-command gains an optional Execution environment reference (RefKind execenv) — empty keeps S1's documented posture (your real login shell and real environment, byte-for-byte); set, the block runs under that ExecEnv through the SAME machinery code-execution uses (shellArgv's clean/login flags, resolveDir's per-block temp-dir mint, explicit-only env with the minimal-PATH default), and block-referenced secrets upsert onto the environment's explicit env, never os.Environ. The Confirm screen names the environment (a secret-free preview seam — never the vault-resolving lookup, so previews can't trigger audit lines). New typedfield contract: OptionalRef marks a RefKind whose empty value is a deliberate posture, skipped by preflight's required-ref check. No new entity: ExecEnv already IS the profile the goal's session answer described. Coding-loop seed revision bumped (its node config grew the resolved default); contract schemas regenerated. Claude-Session: https://claude.ai/code/session_012im1JxQQV2ahnXzZDdVmZq Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent dd5d21b commit ab2e5ec

26 files changed

Lines changed: 382 additions & 21 deletions

File tree

frontend/bindings/github.com/alicoding/mill/internal/adapters/openapispec/models.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ export interface Field {
8080
*/
8181
"RefKind": string;
8282

83+
/**
84+
* OptionalRef marks a RefKind field whose EMPTY value is a legal,
85+
* meaningful configuration rather than "not configured yet"
86+
* (docs/goals/0240 S4's shell-step environment: empty means the
87+
* user's real login shell, a deliberate posture, not a gap) --
88+
* graph preflight skips its required-ref check. Meaningless
89+
* without RefKind. JSON-tagged omitempty for the same
90+
* byte-identical-marshaling reason Deprecated below documents.
91+
*/
92+
"OptionalRef"?: boolean;
93+
8394
/**
8495
* ShowOnCard surfaces this field's value on a card's compact face
8596
* (docs/goals/0152) -- read by Atlas Kinds only; other

frontend/bindings/github.com/alicoding/mill/internal/domain/typedfield/models.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,17 @@ export interface Field {
6666
*/
6767
"RefKind": string;
6868

69+
/**
70+
* OptionalRef marks a RefKind field whose EMPTY value is a legal,
71+
* meaningful configuration rather than "not configured yet"
72+
* (docs/goals/0240 S4's shell-step environment: empty means the
73+
* user's real login shell, a deliberate posture, not a gap) --
74+
* graph preflight skips its required-ref check. Meaningless
75+
* without RefKind. JSON-tagged omitempty for the same
76+
* byte-identical-marshaling reason Deprecated below documents.
77+
*/
78+
"OptionalRef"?: boolean;
79+
6980
/**
7081
* ShowOnCard surfaces this field's value on a card's compact face
7182
* (docs/goals/0152) -- read by Atlas Kinds only; other

frontend/bindings/github.com/alicoding/mill/internal/services/codeloopsvc/models.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ export interface CommandBlockPreview {
99
"shell": string;
1010
"dir": string;
1111

12+
/**
13+
* EnvironmentLabel names the Configure-authored execution
14+
* environment the seeded shell step is configured to run inside
15+
* (docs/goals/0240 S4) -- empty in the default real-login-shell
16+
* posture, where Shell/Dir above already tell the whole story.
17+
*/
18+
"environmentLabel": string;
19+
1220
/**
1321
* GuardrailVerdict is "allow" | "ask" | "deny" -- the block-level
1422
* gate decision (goal 0240 S3): the MOST RESTRICTIVE of every step's

frontend/bindings/github.com/alicoding/mill/internal/services/pluginsvc/pluginservice.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
/**
55
* PluginService is Wails-bound. openURL is injected so tests never
6-
* shell out to the real OS handler.
6+
* shell out to the real OS handler. appVersion is the build-stamped
7+
* Mill version minMillVersion enforcement compares against.
78
* @module
89
*/
910

frontend/src/locales/en/app.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,8 @@
275275
},
276276
"startFailedTitle": "Couldn't start the run",
277277
"cancel": "Cancel",
278-
"run": "Run"
278+
"run": "Run",
279+
"targetEnv": "Runs in the {{env}} environment ({{shell}}, {{dir}})"
279280
},
280281
"running": {
281282
"status": {

frontend/src/shared/CodingLoopConfirmState.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ export function CodingLoopConfirmState({ preview, previewError, startError, type
5555
{t('codingLoop.confirm.summary', { count: stepCount, plural: stepCount === 1 ? '' : 's' })}
5656
</Text>
5757
<Text as="p" size="small" className={styles.target} data-testid="coding-loop-confirm-target">
58-
{t('codingLoop.confirm.target', { shell: preview.shell, dir: preview.dir })}
58+
{preview.environmentLabel
59+
? t('codingLoop.confirm.targetEnv', { env: preview.environmentLabel, shell: preview.shell, dir: preview.dir })
60+
: t('codingLoop.confirm.target', { shell: preview.shell, dir: preview.dir })}
5961
</Text>
6062

6163
<ol className={styles.stepList} data-testid="coding-loop-confirm-steps">

internal/contract/contract.json

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@
8181
"RefKind": {
8282
"type": "string"
8383
},
84+
"OptionalRef": {
85+
"type": "boolean"
86+
},
8487
"ShowOnCard": {
8588
"type": "boolean"
8689
},
@@ -402,6 +405,9 @@
402405
"RefKind": {
403406
"type": "string"
404407
},
408+
"OptionalRef": {
409+
"type": "boolean"
410+
},
405411
"ShowOnCard": {
406412
"type": "boolean"
407413
},
@@ -587,6 +593,9 @@
587593
"RefKind": {
588594
"type": "string"
589595
},
596+
"OptionalRef": {
597+
"type": "boolean"
598+
},
590599
"ShowOnCard": {
591600
"type": "boolean"
592601
},
@@ -1156,6 +1165,9 @@
11561165
"RefKind": {
11571166
"type": "string"
11581167
},
1168+
"OptionalRef": {
1169+
"type": "boolean"
1170+
},
11591171
"ShowOnCard": {
11601172
"type": "boolean"
11611173
},
@@ -2626,8 +2638,24 @@
26262638
"ID": "process-shell-command",
26272639
"Kind": "process",
26282640
"Label": "Run a captured command",
2629-
"Description": "Runs the captured payload in your real login shell, exactly as written -- no sandboxing, no stored environment profile. A piped command stays one step; commands separated by a new line or \u0026\u0026 show as separate steps. External effect -- the run asks for your approval by default. No configurable fields: this node always runs whatever payload it receives.",
2630-
"ConfigFields": null,
2641+
"Description": "Runs the captured payload exactly as written -- in your real login shell by default, or inside a Configure-authored execution environment (its shell, directory, and variables) when one is chosen. A piped command stays one step; commands separated by a new line or \u0026\u0026 show as separate steps. External effect -- the run asks for your approval by default.",
2642+
"ConfigFields": [
2643+
{
2644+
"Key": "envId",
2645+
"Label": "Execution environment",
2646+
"Type": "text",
2647+
"Required": false,
2648+
"Default": "",
2649+
"Description": "Runs the block inside a Configure-authored environment. Empty runs your real login shell.",
2650+
"Options": null,
2651+
"Suggestions": null,
2652+
"Secret": false,
2653+
"RefKind": "execenv",
2654+
"OptionalRef": true,
2655+
"Multiline": false,
2656+
"SystemManaged": false
2657+
}
2658+
],
26312659
"Output": "combined stdout+stderr from every sub-command that ran",
26322660
"Consumes": [
26332661
"text"
@@ -4823,8 +4851,24 @@
48234851
"ID": "process-shell-command",
48244852
"Kind": "process",
48254853
"Label": "Run a captured command",
4826-
"Description": "Runs the captured payload in your real login shell, exactly as written -- no sandboxing, no stored environment profile. A piped command stays one step; commands separated by a new line or \u0026\u0026 show as separate steps. External effect -- the run asks for your approval by default. No configurable fields: this node always runs whatever payload it receives.",
4827-
"ConfigFields": null,
4854+
"Description": "Runs the captured payload exactly as written -- in your real login shell by default, or inside a Configure-authored execution environment (its shell, directory, and variables) when one is chosen. A piped command stays one step; commands separated by a new line or \u0026\u0026 show as separate steps. External effect -- the run asks for your approval by default.",
4855+
"ConfigFields": [
4856+
{
4857+
"Key": "envId",
4858+
"Label": "Execution environment",
4859+
"Type": "text",
4860+
"Required": false,
4861+
"Default": "",
4862+
"Description": "Runs the block inside a Configure-authored environment. Empty runs your real login shell.",
4863+
"Options": null,
4864+
"Suggestions": null,
4865+
"Secret": false,
4866+
"RefKind": "execenv",
4867+
"OptionalRef": true,
4868+
"Multiline": false,
4869+
"SystemManaged": false
4870+
}
4871+
],
48284872
"Output": "combined stdout+stderr from every sub-command that ran",
48294873
"Consumes": [
48304874
"text"

internal/contract/schemas/atlas.schema.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
"RefKind": {
4747
"type": "string"
4848
},
49+
"OptionalRef": {
50+
"type": "boolean"
51+
},
4952
"ShowOnCard": {
5053
"type": "boolean"
5154
},

internal/contract/schemas/decision.schema.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
"RefKind": {
4747
"type": "string"
4848
},
49+
"OptionalRef": {
50+
"type": "boolean"
51+
},
4952
"ShowOnCard": {
5053
"type": "boolean"
5154
},

internal/contract/schemas/list.schema.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
"RefKind": {
4747
"type": "string"
4848
},
49+
"OptionalRef": {
50+
"type": "boolean"
51+
},
4952
"ShowOnCard": {
5053
"type": "boolean"
5154
},

0 commit comments

Comments
 (0)