its-magic 0.1.2-39 → 0.1.2-42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "its-magic",
3
- "version": "0.1.2-39",
3
+ "version": "0.1.2-42",
4
4
  "description": "its-magic - AI dev team workflow for Cursor.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -17,10 +17,14 @@
17
17
  "scripts/intake_bug_routing_guard.py",
18
18
  "scripts/check_intake_template_parity.py",
19
19
  "scripts/materialize_codebase_map.py",
20
+ "scripts/remote_config_summary.py",
21
+ "scripts/guard_installer_publish.py",
20
22
  "bin/its-magic.js",
21
23
  "bin/postinstall.js"
22
24
  ],
23
25
  "scripts": {
26
+ "guard:installer": "python scripts/guard_installer_publish.py",
27
+ "prepublishOnly": "npm run guard:installer",
24
28
  "postinstall": "node bin/postinstall.js",
25
29
  "release:patch": "npm version patch && npm publish",
26
30
  "release:minor": "npm version minor && npm publish",
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env python3
2
+ """Prepublish / CI guard: installer.sh LF + POSIX-safe startup tokens (US-0084 / AC-2).
3
+
4
+ BUG-0008: reject CR bytes in installer-owned-paths.manifest (CRLF breaks POSIX awk section match).
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import shutil
10
+ import subprocess
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ ROOT = Path(__file__).resolve().parents[1]
15
+ INSTALLER_SH = ROOT / "installer.sh"
16
+ INSTALLER_MANIFESTS = (
17
+ ROOT / "docs" / "engineering" / "context" / "installer-owned-paths.manifest",
18
+ ROOT / "template" / "docs" / "engineering" / "context" / "installer-owned-paths.manifest",
19
+ )
20
+
21
+ FORBIDDEN_TOKENS = (
22
+ "set -euo",
23
+ "set -o pipefail",
24
+ "set -eu -o pipefail",
25
+ "set -o errexit",
26
+ "set -o nounset",
27
+ )
28
+
29
+
30
+ def main() -> int:
31
+ if not INSTALLER_SH.is_file():
32
+ print("guard_installer_publish: installer.sh missing", file=sys.stderr)
33
+ return 1
34
+ data = INSTALLER_SH.read_bytes()
35
+ if b"\r" in data:
36
+ print(
37
+ "guard_installer_publish: CR/LF (\\r) bytes found in installer.sh — "
38
+ "use LF only; see docs/engineering/runbook.md (US-0084).",
39
+ file=sys.stderr,
40
+ )
41
+ return 1
42
+ for man in INSTALLER_MANIFESTS:
43
+ if not man.is_file():
44
+ continue
45
+ mdata = man.read_bytes()
46
+ if b"\r" in mdata:
47
+ print(
48
+ f"guard_installer_publish: CR/LF (\\r) bytes found in {man.relative_to(ROOT)} — "
49
+ "use LF only (.gitattributes *.manifest; BUG-0008).",
50
+ file=sys.stderr,
51
+ )
52
+ return 1
53
+ text = data.decode("utf-8", errors="replace")
54
+ for token in FORBIDDEN_TOKENS:
55
+ if token in text:
56
+ print(
57
+ f"guard_installer_publish: forbidden startup token {token!r} in installer.sh",
58
+ file=sys.stderr,
59
+ )
60
+ return 1
61
+ dash = shutil.which("dash")
62
+ if dash:
63
+ r = subprocess.run(
64
+ [dash, "-n", str(INSTALLER_SH)],
65
+ cwd=ROOT,
66
+ capture_output=True,
67
+ text=True,
68
+ encoding="utf-8",
69
+ errors="replace",
70
+ )
71
+ if r.returncode != 0:
72
+ print(
73
+ "guard_installer_publish: dash -n installer.sh failed:\n"
74
+ + (r.stderr or r.stdout or ""),
75
+ file=sys.stderr,
76
+ )
77
+ return 1
78
+ else:
79
+ print(
80
+ "guard_installer_publish: dash not on PATH; skipping dash -n "
81
+ "(Python CRLF + token checks still enforced).",
82
+ file=sys.stderr,
83
+ )
84
+ return 0
85
+
86
+
87
+ if __name__ == "__main__":
88
+ raise SystemExit(main())
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Summarize .cursor/remote.json for operators (US-0084 / US-0064 alignment).
4
+
5
+ Stdout: non-secret labels and env-reference names only (no key material).
6
+ Stderr: errors and skip reasons.
7
+
8
+ Exit codes:
9
+ 0 OK or REMOTE_EXECUTION=0 skip (DEC-0070)
10
+ 1 usage / CLI error
11
+ 2 config missing or unreadable
12
+ 3 invalid JSON
13
+ 4 schema / contract mismatch vs runbook remote.json contract
14
+ 5 reserved (unused; DEC-0070 maps REMOTE_EXECUTION=0 to 0)
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import argparse
20
+ import json
21
+ import os
22
+ import re
23
+ import sys
24
+ from pathlib import Path
25
+ from typing import Any
26
+
27
+ ENV_VAR_NAME = re.compile(r"^[A-Z][A-Z0-9_]*$")
28
+ ALLOWED_TYPES = frozenset({"docker", "ssh", "vm"})
29
+ AUTH_MODES = frozenset({"none", "env"})
30
+
31
+
32
+ def _truthy_remote_execution(raw: str | None) -> bool:
33
+ if raw is None:
34
+ return False
35
+ return raw.strip() in {"1", "true", "TRUE", "yes", "YES", "on", "ON"}
36
+
37
+
38
+ def _parse_args(argv: list[str]) -> tuple[argparse.Namespace, list[str]]:
39
+ p = argparse.ArgumentParser(description=__doc__.split("\n\n")[0], add_help=True)
40
+ p.add_argument(
41
+ "--config",
42
+ default=None,
43
+ help="Path to remote JSON (default: REMOTE_CONFIG env or .cursor/remote.json)",
44
+ )
45
+ return p.parse_known_args(argv)
46
+
47
+
48
+ def _config_path(explicit: str | None) -> Path:
49
+ if explicit:
50
+ return Path(explicit).expanduser()
51
+ env = os.environ.get("REMOTE_CONFIG")
52
+ if env:
53
+ return Path(env).expanduser()
54
+ return Path(".cursor/remote.json")
55
+
56
+
57
+ def _validate_env_ref(name: str, value: Any, prefix: str) -> str | None:
58
+ if not isinstance(value, str):
59
+ return f"{prefix}: {name} must be a string env-reference name"
60
+ if not ENV_VAR_NAME.match(value):
61
+ return f"{prefix}: {name} must look like an env var name (A-Z_0-9)"
62
+ return None
63
+
64
+
65
+ def validate_and_summarize(path: Path, data: dict[str, Any]) -> tuple[list[str], list[str]]:
66
+ """Return (stdout_lines, error_messages)."""
67
+ errs: list[str] = []
68
+ out: list[str] = []
69
+ out.append(f"remote_config={path.as_posix()}")
70
+
71
+ if not isinstance(data.get("version"), int):
72
+ errs.append("[REMOTE_CONFIG_ERROR] root.version: expected integer")
73
+ if not isinstance(data.get("defaultTarget"), str) or not data["defaultTarget"]:
74
+ errs.append("[REMOTE_CONFIG_ERROR] root.defaultTarget: expected non-empty string")
75
+ targets = data.get("targets")
76
+ if not isinstance(targets, list) or not targets:
77
+ errs.append("[REMOTE_CONFIG_ERROR] root.targets: expected non-empty array")
78
+
79
+ if errs:
80
+ return out, errs
81
+
82
+ by_id: dict[str, dict[str, Any]] = {}
83
+ for i, t in enumerate(targets):
84
+ prefix = f"targets[{i}]"
85
+ if not isinstance(t, dict):
86
+ errs.append(f"[REMOTE_CONFIG_ERROR] {prefix}: expected object")
87
+ continue
88
+ tid = t.get("id")
89
+ if not isinstance(tid, str) or not tid:
90
+ errs.append(f"[REMOTE_CONFIG_ERROR] {prefix}.id: expected non-empty string")
91
+ ttype = t.get("type")
92
+ if ttype not in ALLOWED_TYPES:
93
+ errs.append(
94
+ f"[REMOTE_CONFIG_ERROR] {prefix}.type: expected one of {sorted(ALLOWED_TYPES)}"
95
+ )
96
+ if not isinstance(t.get("enabled"), bool):
97
+ errs.append(f"[REMOTE_CONFIG_ERROR] {prefix}.enabled: expected boolean")
98
+ if not isinstance(t.get("host"), str) or not t["host"]:
99
+ errs.append(f"[REMOTE_CONFIG_ERROR] {prefix}.host: expected non-empty string")
100
+ port = t.get("port")
101
+ if not isinstance(port, int) or not (1 <= port <= 65535):
102
+ errs.append(f"[REMOTE_CONFIG_ERROR] {prefix}.port: expected integer 1..65535")
103
+ if not isinstance(t.get("workspaceRoot"), str) or not t["workspaceRoot"]:
104
+ errs.append(
105
+ f"[REMOTE_CONFIG_ERROR] {prefix}.workspaceRoot: expected non-empty string"
106
+ )
107
+ auth = t.get("auth")
108
+ if auth is not None:
109
+ if not isinstance(auth, dict):
110
+ errs.append(f"[REMOTE_CONFIG_ERROR] {prefix}.auth: expected object")
111
+ else:
112
+ mode = auth.get("mode")
113
+ if mode not in AUTH_MODES:
114
+ errs.append(
115
+ f"[REMOTE_CONFIG_ERROR] {prefix}.auth.mode: expected one of {sorted(AUTH_MODES)}"
116
+ )
117
+ elif mode == "env":
118
+ env_keys = (
119
+ "tokenEnv",
120
+ "passwordEnv",
121
+ "privateKeyPathEnv",
122
+ "usernameEnv",
123
+ )
124
+ any_ref = False
125
+ for k in env_keys:
126
+ if k not in auth:
127
+ continue
128
+ any_ref = True
129
+ err = _validate_env_ref(k, auth[k], f"{prefix}.auth")
130
+ if err:
131
+ errs.append(f"[REMOTE_CONFIG_ERROR] {err}")
132
+ if not any_ref:
133
+ errs.append(
134
+ f"[REMOTE_CONFIG_ERROR] {prefix}.auth: mode=env requires at least one *Env field"
135
+ )
136
+ if isinstance(tid, str) and tid:
137
+ by_id[tid] = t
138
+
139
+ default = data["defaultTarget"]
140
+ if default not in by_id:
141
+ errs.append(
142
+ f"[REMOTE_CONFIG_ERROR] defaultTarget={default!r} not found in targets[].id"
143
+ )
144
+ elif not by_id[default].get("enabled"):
145
+ errs.append(
146
+ f"[REMOTE_CONFIG_ERROR] defaultTarget={default!r} must reference an enabled target"
147
+ )
148
+
149
+ if errs:
150
+ return out, errs
151
+
152
+ out.append(f"defaultTarget={data['defaultTarget']}")
153
+ for t in targets:
154
+ assert isinstance(t, dict)
155
+ tid = t["id"]
156
+ parts = [
157
+ f"id={tid}",
158
+ f"type={t['type']}",
159
+ f"enabled={t['enabled']!s}",
160
+ f"host={t['host']}",
161
+ f"port={t['port']}",
162
+ f"workspaceRoot={t['workspaceRoot']}",
163
+ ]
164
+ auth = t.get("auth")
165
+ if isinstance(auth, dict):
166
+ parts.append(f"auth.mode={auth.get('mode')}")
167
+ for k in (
168
+ "tokenEnv",
169
+ "passwordEnv",
170
+ "privateKeyPathEnv",
171
+ "usernameEnv",
172
+ ):
173
+ if k in auth and isinstance(auth[k], str):
174
+ parts.append(f"auth.{k}={auth[k]}")
175
+ out.append("target:" + " ".join(parts))
176
+
177
+ return out, []
178
+
179
+
180
+ def main(argv: list[str] | None = None) -> int:
181
+ args, rest = _parse_args(argv or sys.argv[1:])
182
+ if rest:
183
+ print(
184
+ f"[REMOTE_CONFIG_SUMMARY] unknown arguments: {' '.join(rest)}",
185
+ file=sys.stderr,
186
+ )
187
+ return 1
188
+ if not _truthy_remote_execution(os.environ.get("REMOTE_EXECUTION")):
189
+ print(
190
+ "[REMOTE_CONFIG_SUMMARY] REMOTE_EXECUTION!=1: skip validation/summary "
191
+ "(zero overhead; DEC-0070 / US-0084).",
192
+ file=sys.stderr,
193
+ )
194
+ return 0
195
+
196
+ path = _config_path(args.config)
197
+ if not path.is_file():
198
+ print(
199
+ f"[REMOTE_CONFIG_ERROR] {path}: file missing or unreadable. "
200
+ "Fix: create config or set REMOTE_EXECUTION=0.",
201
+ file=sys.stderr,
202
+ )
203
+ return 2
204
+ try:
205
+ raw = path.read_text(encoding="utf-8")
206
+ except OSError as e:
207
+ print(
208
+ f"[REMOTE_CONFIG_ERROR] {path}: read failed ({e}). Fix: permissions/path.",
209
+ file=sys.stderr,
210
+ )
211
+ return 2
212
+ try:
213
+ data = json.loads(raw)
214
+ except json.JSONDecodeError as e:
215
+ print(
216
+ f"[REMOTE_CONFIG_ERROR] {path}: invalid JSON ({e}). Fix: syntax.",
217
+ file=sys.stderr,
218
+ )
219
+ return 3
220
+ if not isinstance(data, dict):
221
+ print(
222
+ f"[REMOTE_CONFIG_ERROR] {path}: expected JSON object at root.",
223
+ file=sys.stderr,
224
+ )
225
+ return 4
226
+
227
+ lines, errs = validate_and_summarize(path, data)
228
+ if errs:
229
+ for line in errs:
230
+ print(line, file=sys.stderr)
231
+ print(
232
+ f"[REMOTE_CONFIG_ERROR] {path}: contract check failed. "
233
+ "Fix: align with docs/engineering/runbook.md remote contract (US-0084).",
234
+ file=sys.stderr,
235
+ )
236
+ return 4
237
+ for line in lines:
238
+ print(line)
239
+ return 0
240
+
241
+
242
+ if __name__ == "__main__":
243
+ raise SystemExit(main())
@@ -19,6 +19,11 @@ description: "its-magic auto: deterministic continuation orchestrator."
19
19
  - Phase context transfer happens only through artifacts and handoff files.
20
20
  - Scope is process/workflow orchestration only. Do not claim runtime product
21
21
  orchestration changes.
22
+ - **Bug-queue mode** (**`US-0087`**) uses the same **spawn-only** contract: the
23
+ orchestrator schedules materialization and spawns phase-role subagents per
24
+ bug segment—it **must not** run **`execute`**, **`qa`**, or other lifecycle
25
+ phases in the orchestrator turn. Violations → **`AUTO_ORCHESTRATOR_PHASE_EXECUTION`**
26
+ (**`BUG-0006`**, **`US-0069`**, **`DEC-0051`**).
22
27
 
23
28
  ## Spawn-boundary integrity (BUG-0006)
24
29
 
@@ -33,6 +38,39 @@ description: "its-magic auto: deterministic continuation orchestrator."
33
38
  (wrong writer / isolation break) and **`RUNTIME_PROOF_*`** / **`PHASE_ROLE_*`**
34
39
  families—do not overload those codes for a missing-spawn violation.
35
40
 
41
+ ## Continuous multi-phase execution (US-0088)
42
+
43
+ A single `/auto` orchestrated run advances through **all phases** in the
44
+ **intersected resolved schedule** (reference **Step 5**) until a
45
+ **deterministic stop condition** fires. The orchestrator does **not** stop after
46
+ spawning one phase unless the stop matrix requires it.
47
+
48
+ **Outer-driver equivalence (AC-1, Option B)**: When a single Cursor `/auto`
49
+ invocation cannot schedule multiple fresh subagent turns (product/runtime
50
+ constraint), a **documented outer driver** (operator script or manual
51
+ re-invocation with `start-from` / refreshed `resume_brief`) is
52
+ **deterministically equivalent** provided: same intersected phase order, same
53
+ isolation + strict-proof attestation per phase (**DEC-0038**), same stop
54
+ reasons, and same `resume_brief` + `state.md` refresh at every boundary.
55
+ Operators must follow the runbook recipe
56
+ (**`docs/engineering/runbook.md`** § Continuous `/auto` + backlog drain).
57
+
58
+ **Deterministic stop matrix** (see also architecture `# US-0088`):
59
+
60
+ | Condition | Behavior |
61
+ |-----------|----------|
62
+ | Next phase exists, no hard stop | **Continue** — preflight US-0069, spawn next phase |
63
+ | `decision_gate` | **Stop** (non-suppressible) |
64
+ | `error` / missing critical input | **Stop** (non-suppressible) |
65
+ | `AUTO_PAUSE_REQUEST` / `pause` | **Stop** at safe boundary (non-suppressible) |
66
+ | `AUTO_LOOP_MAX_CYCLES` / `loop_max` | **Stop** (non-suppressible) |
67
+ | `blocked` (sync/scope gate) | **Stop** (non-suppressible) |
68
+ | US lifecycle DONE / sprint segment complete | **Stop** segment; `AUTO_BACKLOG_DRAIN=1` may advance to next OPEN story (recompute phase plan — **reference Step 5**) |
69
+ | `BACKLOG_MAX_STORIES_REACHED` | **Stop** (non-suppressible) |
70
+
71
+ `stop_reason` vocabulary: `completed`, `decision_gate`, `missing_input`,
72
+ `pause_request`, `loop_max`, `error`, `blocked`.
73
+
36
74
  ## Full specification (US-0080 / DEC-0062)
37
75
 
38
76
  Long prose, expanded mode semantics, and **Steps 1–13** detail live in
@@ -101,16 +139,37 @@ Selectors and reinstatement: see reference. Phase-plan reason codes include
101
139
  `PHASE_POLICY_CONFLICT`, `PHASE_PLAN_UNKNOWN_PHASE`, `START_FROM_PHASE_PLAN_EMPTY_INTERSECTION`.
102
140
 
103
141
  Phase boundary visibility (**AC-10**): record `resolved_phase_plan` snapshot,
104
- `skipped_phases`, `phase_boundary`, `next_scheduled_phase` on `state.md`.
142
+ `skipped_phases`, `phase_boundary`, `next_scheduled_phase` on `state.md`. For
143
+ bug-queue segments, also record **`segment_work_item_kind`**, **`active_bug_id`**,
144
+ **`bug_queue_position`**, **`bug_queue_remaining`**, **`backlog_drain_active`**,
145
+ **`bug_queue_active`** per **`docs/engineering/architecture.md`** **`# US-0087`**
146
+ and **`docs/engineering/auto-orchestration-reference.md`**.
105
147
 
106
148
  ## Inputs
107
149
 
108
150
  Merged scratchpad (**US-0073** / **DEC-0055**), automation flags (`AUTO_*`, `SECURITY_REVIEW`,
109
151
  `TEAM_*`), phase-plan keys `AUTO_PHASE_PLAN`, `AUTO_PHASE_EXCLUDE`, `AUTO_PHASE_INCLUDE`,
110
152
  `AUTO_PHASE_PROFILE`, `AUTO_PHASE_HIGH_RISK_ACK`, product/engineering docs,
111
- optional `start-from=<phase>`, optional `--execute-bulk`, `handoffs/resume_brief.md`,
153
+ optional `start-from=<phase>`, optional **`bug-target=BUG-####`** or
154
+ **`bug-target=all-open`**, optional `--execute-bulk`, `handoffs/resume_brief.md`,
112
155
  `docs/engineering/state.md`.
113
156
 
157
+ ## Automation remote routing contract (US-0086)
158
+
159
+ - Automation-only gate: `AUTO_REMOTE_AUTOMATION_PROFILE=deterministic_v1` enables
160
+ target routing; `off` keeps manual/local behavior unchanged.
161
+ - Explicit intent literal is constrained to: `start container <target_id>`.
162
+ - Deterministic precedence when profile is enabled:
163
+ 1. explicit intent target id resolution,
164
+ 2. canonical target validation (`targets[].id` exists and is enabled),
165
+ 3. documented heuristic fallback,
166
+ 4. local default when no remote target is selected.
167
+ - Fail-closed reason codes (do not overload):
168
+ `REMOTE_AUTOMATION_MODE_OFF`, `REMOTE_TARGET_UNKNOWN`,
169
+ `REMOTE_TARGET_DISABLED`, `REMOTE_TARGET_UNROUTABLE`.
170
+ - Mode-off guardrail: never silently reroute `TEST_COMMAND` to remote when
171
+ automation profile is disabled.
172
+
114
173
  ## Canonical status contract (US-0045)
115
174
 
116
175
  Story status authority: `docs/product/backlog.md` only; do not infer readiness from
@@ -124,14 +183,43 @@ QA loop handoffs when applicable, continuation breadcrumbs including `resolution
124
183
 
125
184
  ## Stop conditions
126
185
 
127
- Decision gate, missing critical input, `AUTO_PAUSE_REQUEST` at safe boundary,
128
- `AUTO_LOOP_MAX_CYCLES` with unresolved defects.
186
+ Deterministic stop reasons (see **Stop matrix** in `## Continuous multi-phase
187
+ execution (US-0088)` above): `completed`, `decision_gate`, `missing_input`,
188
+ `pause_request`, `loop_max`, `error`, `blocked`.
129
189
 
130
190
  ## Optional backlog-drain mode (US-0044 / DEC-0022)
131
191
 
132
192
  Canonical controls: `AUTO_BACKLOG_DRAIN`, `AUTO_BACKLOG_MAX_STORIES`, `AUTO_BACKLOG_ON_BLOCK`,
133
- `AUTO_STORY_SELECTION`. Reason codes include `BACKLOG_MAX_STORIES_REACHED`. Full semantics:
134
- reference.
193
+ `AUTO_STORY_SELECTION`. When `AUTO_BACKLOG_DRAIN=1`, each story advances through
194
+ **multiple phases** until its terminal boundary (**reference Step 5**); the
195
+ orchestrator **recomputes** the materialized phase plan at each **story boundary**
196
+ and selects the **next eligible OPEN story** per `AUTO_STORY_SELECTION`.
197
+ Reason codes include `BACKLOG_MAX_STORIES_REACHED`. Full semantics: reference.
198
+
199
+ ## Optional bug-queue mode (US-0087)
200
+
201
+ Canonical **argv** literals (exact strings; **no aliases** in v1):
202
+ - **`bug-target=BUG-####`** (example: **`bug-target=BUG-0007`**) — single defect from
203
+ **`docs/product/backlog.md`** **`## Bug issues (canonical)`** with status **OPEN**.
204
+ - **`bug-target=all-open`** — deterministic **OPEN**-only queue, ascending **numeric**
205
+ **`BUG-####`** sort, optional cap **`AUTO_BUG_MAX_ITEMS`** (see reference).
206
+
207
+ Scratchpad keys (**default-off**): **`AUTO_BUG_QUEUE`**, **`AUTO_BUG_TARGET`**,
208
+ **`AUTO_BUG_MAX_ITEMS`**, **`AUTO_BUG_ON_BLOCK`** — full semantics: reference +
209
+ **`architecture.md`** **`# US-0087`**.
210
+
211
+ **Scheduler mutex**: if merged scratchpad has **`AUTO_BACKLOG_DRAIN=1`** **and**
212
+ **`AUTO_BUG_QUEUE=1`** **and** this invocation has **no** explicit **`bug-target=`**
213
+ argv token → fail closed with **`AUTO_SCHEDULER_CONFLICT`** (use
214
+ **`[AUTO_RESUME_ERROR] AUTO_SCHEDULER_CONFLICT: ...`** form per reference). When
215
+ **`bug-target=`** argv is present, it **selects** the bug scheduler for this run;
216
+ **`AUTO_BACKLOG_DRAIN`** must **not** also drive story selection for that same
217
+ materialized run.
218
+
219
+ Fail-closed codes (orthogonal to existing resume/phase codes; do **not** overload):
220
+ - **`AUTO_BUG_QUEUE_EMPTY`** — **`all-open`** (or equivalent) and zero **OPEN** bugs.
221
+ - **`AUTO_BUG_TARGET_UNKNOWN`** — malformed id, wrong pattern, or id missing from canonical bug section.
222
+ - **`AUTO_BUG_TARGET_NOT_OPEN`** — known id exists but status is not **OPEN** (e.g. **DONE**).
135
223
 
136
224
  ## Optional bulk execute mode (US-0047 / DEC-0024)
137
225
 
@@ -152,12 +240,19 @@ Phase-completion boundary evaluation only. **Guarded auto-push eligibility chain
152
240
 
153
241
  ## Deterministic resume-source precedence
154
242
 
155
- Resolve start phase in strict order:
243
+ Resolve nominal start phase and scheduler inputs in strict order (**`US-0087`**
244
+ extends scratchpad vs **`resume_brief`** ordering — full matrix: reference):
156
245
 
157
246
  1. Explicit `/auto start-from=<phase>`
158
- 2. `handoffs/resume_brief.md`
159
- 3. Conservative `docs/engineering/state.md` fallback
160
- 4. Fail fast on ambiguity/conflict/unrecoverable inputs
247
+ 2. Explicit **`bug-target=`** argv token when present (parsed **before** merged
248
+ scratchpad scheduler keys; selects bug scheduler for this run).
249
+ 3. Merged scratchpad (**`US-0073`** / **`DEC-0055`**) — including **`AUTO_BACKLOG_DRAIN`**,
250
+ **`AUTO_BUG_QUEUE`**, **`AUTO_BUG_TARGET`**, etc.
251
+ 4. `handoffs/resume_brief.md`
252
+ 5. Conservative `docs/engineering/state.md` fallback
253
+ 6. Fail fast on ambiguity/conflict/unrecoverable inputs (including
254
+ **`AUTO_SCHEDULER_CONFLICT`** when both schedulers are enabled in scratchpad
255
+ without **`bug-target=`** argv resolution).
161
256
 
162
257
  If `resume_brief.md` is present but stale or unparseable, fail fast instead
163
258
  of silently falling back.
@@ -177,22 +272,42 @@ Required codes:
177
272
  - `STATE_PHASE_AMBIGUOUS`
178
273
  - `STATE_PHASE_UNRECOVERABLE`
179
274
 
275
+ Bug-queue extensions (**`US-0087`**; same **`[AUTO_RESUME_ERROR]`** envelope when
276
+ used for resume/materialization failures):
277
+
278
+ - `AUTO_SCHEDULER_CONFLICT`
279
+ - `AUTO_BUG_QUEUE_EMPTY`
280
+ - `AUTO_BUG_TARGET_UNKNOWN`
281
+ - `AUTO_BUG_TARGET_NOT_OPEN`
282
+
180
283
  ## Steps (compact; full detail in reference)
181
284
 
182
285
  1. Read automation flags from merged scratchpad and **materialize the resolved
183
286
  phase plan** per **Configurable phase selection policy (US-0070 / DEC-0052)**; append
184
287
  plan breadcrumbs to `docs/engineering/state.md` **before** first spawn.
185
- 2. Parse `start-from` / `--execute-bulk`; resolve nominal start phase; intersect with plan.
288
+ 2. Parse `start-from` / **`bug-target=`** / `--execute-bulk`; resolve scheduler
289
+ mutex (**`AUTO_SCHEDULER_CONFLICT`** when applicable); resolve nominal start phase;
290
+ intersect with plan.
186
291
  3. Record continuation metadata (`invocation_mode=auto`, `requested_start_from`,
187
292
  `resolved_start_phase`, `resolution_source`, `resolution_status`, `timestamp`).
188
293
  4. Spawn fresh subagents per intersected schedule; enforce **US-0069** preflight/post checks.
189
- 5. Implementation loop, pause, stop breadcrumbs (`stop_reason` such as `completed|decision_gate|missing_input|pause_request|loop_max`, `stop_phase`, `timestamp`), `resume_brief` updates — reference.
190
- 6. 11a. Isolation evidence verification at each boundary.
191
- 7. 11b. At each phase boundary, verify strict runtime attestation tuple exists
294
+ 5. **Multi-phase continuation** (normative detail: **reference Step 5** in
295
+ **`docs/engineering/auto-orchestration-reference.md`** `## Steps` item 5):
296
+ advance through **all remaining phases** in the intersected resolved schedule
297
+ order until a **deterministic stop condition** fires (see **Stop matrix** in
298
+ `## Continuous multi-phase execution (US-0088)` above). When
299
+ `AUTO_BACKLOG_DRAIN=1`, repeat the story lifecycle for the next eligible OPEN
300
+ story, **reloading** scratchpad and **recomputing** the materialized phase
301
+ plan at each story boundary. Outer-driver equivalence applies when a single
302
+ invocation cannot schedule multiple subagent turns (**AC-1 Option B**).
303
+ `stop_reason`: `completed|decision_gate|missing_input|pause_request|loop_max|error|blocked`.
304
+ 6. Isolation evidence verification at each boundary (**reference** step 11a).
305
+ 7. At each phase boundary, verify strict runtime attestation tuple exists
192
306
  and is valid for the completed phase (`orchestrator_run_id`, `runtime_proof_id`,
193
- `phase_id`, `role`, `proof_issued_at`, `proof_ttl_seconds`, `proof_hash`).
194
- 8. Sync verdict recording when eligible — reference.
195
- 9. Backlog-drain / bulk per-item summaries when enabled — reference.
307
+ `phase_id`, `role`, `proof_issued_at`, `proof_ttl_seconds`, `proof_hash`)
308
+ (**reference** step 11b).
309
+ 8. Sync verdict recording when eligible — reference step 12.
310
+ 9. Backlog-drain / bulk per-item summaries when enabled — reference step 13.
196
311
 
197
312
  ## Backward compatibility
198
313
 
@@ -152,6 +152,12 @@ parity for listed paths: **`python scripts/check_token_cost_parity.py --repo .`*
152
152
  fail fast with `REMOTE_CONNECTIVITY_CONFIG_INVALID`.
153
153
  - Never expose secrets in execution outputs; only sanitized endpoint data and
154
154
  env-reference names are allowed.
155
+ 17b. Remote evidence cues (US-0084):
156
+ - When `REMOTE_EXECUTION=1`, cite an **environment label** in
157
+ `handoffs/dev_to_qa.md` (e.g. `WSL`, `ssh:SSH_HOST` as the **env var name**,
158
+ `dockerOverSsh`) and state **where tests ran** (local vs remote host).
159
+ - Do not paste private keys, tokens, or resolved secret **values**; use
160
+ `python scripts/remote_config_summary.py` for a names-only summary when needed.
155
161
  18. Runtime QA autopilot execution contract (US-0065 / DEC-0047):
156
162
  - Treat runtime verification as mandatory for generated-project scope; static
157
163
  checks alone are not sufficient evidence for PASS readiness.
@@ -114,6 +114,11 @@ verify no unresolved blockers.
114
114
  - If remote connectivity config is incomplete for required remote checks,
115
115
  mark blocking with deterministic reason code
116
116
  `REMOTE_CONNECTIVITY_CONFIG_INVALID`.
117
+ - **US-0084**: when `REMOTE_EXECUTION=1`, expect **`handoffs/dev_to_qa.md`**
118
+ to carry an **environment label** (`WSL`, `ssh:<hostEnv>`, `dockerOverSsh`, …)
119
+ and **test locus** (local vs remote); reject evidence that pastes secret
120
+ values (keys/passwords) — **names-only** refs align with
121
+ `python scripts/remote_config_summary.py` output.
117
122
  13. Runtime QA autopilot contract (US-0065 / DEC-0047):
118
123
  - Runtime truth path is mandatory for generated-project QA:
119
124
  `startup -> readiness/connectivity -> log scan -> bounded retry -> verdict`.
@@ -26,6 +26,13 @@ globs: ["**/*"]
26
26
  - Remote config security (DEC-0016): never commit secret literals (tokens,
27
27
  passwords, private keys) in `.cursor/remote.json`; use environment-variable
28
28
  references only (for example `tokenEnv`, `passwordEnv`, `privateKeyPathEnv`).
29
+ - `.env` exclusion (DEC-0071 / US-0085): do not open, attach, read, search
30
+ inside, or index `.env` or `.env.*` files. Use environment variable names
31
+ in prose only. Operators source `.env` outside agent context.
32
+ - Automation remote routing (US-0086): when `AUTO_REMOTE_AUTOMATION_PROFILE=off`,
33
+ keep local behavior and never silently reroute `TEST_COMMAND` to remote.
34
+ Explicit NL routing is limited to `start container <target_id>` and unknown or
35
+ disabled targets must fail closed with documented reason codes.
29
36
  - Performance by default: avoid obvious N+1 loops, repeated expensive work, and
30
37
  unnecessary I/O in hot paths.
31
38
  - Documentation by default: update relevant docs when behavior, setup, or usage
@@ -41,6 +41,17 @@ MAGIC_BENCH_SESSION=
41
41
  # - AUTO_EXECUTE_ON_BLOCK: stop|skip (behavior when a planned item blocks)
42
42
  # - AUTO_EXECUTE_SELECTION: planned_then_priority
43
43
  # - AUTO_TEAM_SCOPE_ENFORCE: 0|1 (when TEAM_MODE=1, enforce TEAM_MEMBER + ACTIVE_TASK_IDS)
44
+ # Optional bug-queue mode (US-0087) — default-off when absent/unset after merge
45
+ # - AUTO_BUG_QUEUE: 0|1 (1 = enable bug-targeted /auto; mutex vs AUTO_BACKLOG_DRAIN without bug-target argv)
46
+ # - AUTO_BUG_TARGET: all-open|BUG-#### (required when AUTO_BUG_QUEUE=1 unless bug-target= argv supplies target)
47
+ # - AUTO_BUG_MAX_ITEMS: non-negative integer (0 or unset = no cap for all-open queue per run)
48
+ # - AUTO_BUG_ON_BLOCK: stop|skip (bug segment pause/stop boundary)
49
+ # Quiet mode (US-0088) — suppress routine per-phase success chatter only
50
+ # - AUTO_QUIET: 0|1 (default 0; 1 = quiet routine notifications)
51
+ # Non-suppressible: decision_gate, errors, pause, loop_max, blocked, missing inputs.
52
+ # Orthogonal to TOKEN_PROFILE (DEC-0035 / US-0080) — TOKEN_PROFILE controls
53
+ # context breadth / token cost, not notification policy.
54
+ AUTO_QUIET=0
44
55
  AUTO_FLOW_MODE=auto_until_decision
45
56
  PHASE_MODE=interactive
46
57
  PERMISSION_MODE=interactive
@@ -55,6 +66,10 @@ AUTO_EXECUTE_MAX_ITEMS=1
55
66
  AUTO_EXECUTE_ON_BLOCK=stop
56
67
  AUTO_EXECUTE_SELECTION=planned_then_priority
57
68
  AUTO_TEAM_SCOPE_ENFORCE=1
69
+ AUTO_BUG_QUEUE=0
70
+ AUTO_BUG_TARGET=
71
+ AUTO_BUG_MAX_ITEMS=0
72
+ AUTO_BUG_ON_BLOCK=stop
58
73
  #
59
74
  # `/auto` phase role policy (US-0069 / DEC-0051)
60
75
  # - AUTO_ROLE_RESEARCH: po|tech-lead (empty -> default tech-lead)
@@ -101,11 +116,15 @@ SPRINT_BULK_MAX_STORIES=5
101
116
  SPRINT_BULK_MAX_SPRINTS=3
102
117
  SPRINT_BULK_SELECTION=priority_then_backlog_order
103
118
  #
104
- # Remote execution
119
+ # Remote execution (US-0086 / US-0084 / US-0064)
105
120
  # - REMOTE_EXECUTION: 0|1
106
121
  # - REMOTE_CONFIG: path to remote config
122
+ # - AUTO_REMOTE_AUTOMATION_PROFILE: off|deterministic_v1 (default off/manual-safe)
123
+ # - AUTO_REMOTE_ENVIRONMENT_LABEL: local|docker|ssh (names-only evidence label)
107
124
  REMOTE_EXECUTION=0
108
125
  REMOTE_CONFIG=.cursor/remote.json
126
+ AUTO_REMOTE_AUTOMATION_PROFILE=off
127
+ AUTO_REMOTE_ENVIRONMENT_LABEL=local
109
128
  #
110
129
  # Sync policy
111
130
  # - SYNC_POLICY_MODE: disabled|manual|by_phase|by_milestone|custom_phase_list