its-magic 0.1.2-38 → 0.1.2-40

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-38",
3
+ "version": "0.1.2-40",
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",
@@ -12,6 +12,7 @@ INTAKE_TEMPLATE_PAIRS: tuple[tuple[str, str], ...] = (
12
12
  ("scripts/intake_evidence_validate.py", "template/scripts/intake_evidence_validate.py"),
13
13
  ("scripts/intake_evidence_lib.py", "template/scripts/intake_evidence_lib.py"),
14
14
  ("scripts/intake_bug_routing_guard.py", "template/scripts/intake_bug_routing_guard.py"),
15
+ ("scripts/intake_bug_resume_brief_refresh.py", "template/scripts/intake_bug_resume_brief_refresh.py"),
15
16
  ("scripts/check_intake_template_parity.py", "template/scripts/check_intake_template_parity.py"),
16
17
  )
17
18
 
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env python3
2
+ """Prepublish / CI guard: installer.sh LF + POSIX-safe startup tokens (US-0084 / AC-2)."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import shutil
7
+ import subprocess
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ ROOT = Path(__file__).resolve().parents[1]
12
+ INSTALLER_SH = ROOT / "installer.sh"
13
+
14
+ FORBIDDEN_TOKENS = (
15
+ "set -euo",
16
+ "set -o pipefail",
17
+ "set -eu -o pipefail",
18
+ "set -o errexit",
19
+ "set -o nounset",
20
+ )
21
+
22
+
23
+ def main() -> int:
24
+ if not INSTALLER_SH.is_file():
25
+ print("guard_installer_publish: installer.sh missing", file=sys.stderr)
26
+ return 1
27
+ data = INSTALLER_SH.read_bytes()
28
+ if b"\r" in data:
29
+ print(
30
+ "guard_installer_publish: CR/LF (\\r) bytes found in installer.sh — "
31
+ "use LF only; see docs/engineering/runbook.md (US-0084).",
32
+ file=sys.stderr,
33
+ )
34
+ return 1
35
+ text = data.decode("utf-8", errors="replace")
36
+ for token in FORBIDDEN_TOKENS:
37
+ if token in text:
38
+ print(
39
+ f"guard_installer_publish: forbidden startup token {token!r} in installer.sh",
40
+ file=sys.stderr,
41
+ )
42
+ return 1
43
+ dash = shutil.which("dash")
44
+ if dash:
45
+ r = subprocess.run(
46
+ [dash, "-n", str(INSTALLER_SH)],
47
+ cwd=ROOT,
48
+ capture_output=True,
49
+ text=True,
50
+ encoding="utf-8",
51
+ errors="replace",
52
+ )
53
+ if r.returncode != 0:
54
+ print(
55
+ "guard_installer_publish: dash -n installer.sh failed:\n"
56
+ + (r.stderr or r.stdout or ""),
57
+ file=sys.stderr,
58
+ )
59
+ return 1
60
+ else:
61
+ print(
62
+ "guard_installer_publish: dash not on PATH; skipping dash -n "
63
+ "(Python CRLF + token checks still enforced).",
64
+ file=sys.stderr,
65
+ )
66
+ return 0
67
+
68
+
69
+ if __name__ == "__main__":
70
+ raise SystemExit(main())
@@ -1,6 +1,6 @@
1
1
  """
2
2
  Deterministic intake evidence validation
3
- (US-0078 / US-0083 / DEC-0060 / DEC-0067 / R-0055).
3
+ (US-0078 / US-0083 / DEC-0060 / DEC-0067 / R-0055 / BUG-0007 / R-0066).
4
4
 
5
5
  Consumes a logical intake_evidence bundle (dict). PO workflows MUST run this
6
6
  gate before mutating backlog/acceptance; failures are fail-closed.
@@ -202,6 +202,77 @@ def _row_uses_equivalent_evidence(row: dict[str, Any]) -> bool:
202
202
  return bool(str(row.get("equivalent_evidence_ref") or "").strip())
203
203
 
204
204
 
205
+ def _norm_answer_ref_quoted_user_text(value: Any) -> str:
206
+ """Normalize quoted_user_text for BUG-0007 duplicate detection (DEC-0060 strip parity)."""
207
+ if value is None:
208
+ return ""
209
+ return str(value).strip()
210
+
211
+
212
+ def _row_exempt_from_answer_ref_topic_distinctness(row: dict[str, Any]) -> bool:
213
+ """BUG-0007: alternate satisfaction paths do not participate in answer_ref blob reuse checks."""
214
+ return _row_uses_equivalent_evidence(row)
215
+
216
+
217
+ def _validate_answer_ref_topic_distinctness(
218
+ bundle: dict[str, Any],
219
+ required: list[str],
220
+ by_key: dict[str, dict[str, Any]],
221
+ res: ValidationResult,
222
+ ) -> None:
223
+ """
224
+ BUG-0007 / R-0066: distinct required topic_key rows must not reuse the same
225
+ quoted_user_text under satisfied_by=answer_ref (normalized), except exempt rows.
226
+ """
227
+ norm_to_topics: dict[str, list[str]] = {}
228
+ for k in required:
229
+ row = by_key.get(k)
230
+ if not row:
231
+ continue
232
+ sat = (row.get("satisfied_by") or "").strip()
233
+ if sat != "answer_ref":
234
+ continue
235
+ if _row_exempt_from_answer_ref_topic_distinctness(row):
236
+ continue
237
+ irid = _row_run_id(bundle, row)
238
+ tit = _row_turn(row)
239
+ if irid is None or tit is None:
240
+ continue
241
+ qraw = row.get("quoted_user_text")
242
+ qtxt = "" if qraw is None else str(qraw)
243
+ ref = (row.get("ref") or "").strip()
244
+ if not ref or not verify_ie_ref(
245
+ ref,
246
+ intake_run_id=irid,
247
+ turn_index=int(tit),
248
+ topic_key=k,
249
+ satisfied_by=sat,
250
+ quoted_user_text=qtxt,
251
+ ):
252
+ continue
253
+ norm = _norm_answer_ref_quoted_user_text(qraw)
254
+ norm_to_topics.setdefault(norm, []).append(k)
255
+
256
+ dup_groups: list[str] = []
257
+ for norm, topics in norm_to_topics.items():
258
+ uniq = sorted(set(topics))
259
+ if len(uniq) < 2:
260
+ continue
261
+ dup_groups.append("text=" + repr(norm[:120]) + " topics=" + ",".join(uniq))
262
+
263
+ if dup_groups:
264
+ res.ok = False
265
+ res.add_code("INTAKE_ANSWER_REF_NOT_TOPIC_DISTINCT")
266
+ res.diagnostics.append(
267
+ "Remediation: distinct required topics must not reuse the same quoted_user_text "
268
+ "under satisfied_by=answer_ref (BUG-0007 / R-0066). Duplicates: "
269
+ + "; ".join(dup_groups)
270
+ + ". Use per-topic answers, or an allowed alternate path "
271
+ "(evidence_source=equivalent_evidence_ref + equivalent_evidence_ref, "
272
+ "delegation_ref per DEC-0067 / US-0083, or assumption_confirmation_ref on the row)."
273
+ )
274
+
275
+
205
276
  def _candidate_story_ids(bundle: dict[str, Any]) -> set[str]:
206
277
  out: set[str] = set()
207
278
 
@@ -527,6 +598,8 @@ def validate_intake_evidence(
527
598
  res.missing_topics.append(k)
528
599
  res.missing_topics = sorted(set(res.missing_topics))
529
600
 
601
+ _validate_answer_ref_topic_distinctness(bundle, required, by_key, res)
602
+
530
603
  # US-0081 / DEC-0064: first/new/broad intake requires complete-plan coverage contract.
531
604
  if pack == "first-intake-pack":
532
605
  _validate_plan_coverage_contract(bundle, res)
@@ -668,6 +741,32 @@ def self_test() -> None:
668
741
  assert d0.ok and d1.ok
669
742
  assert d0.primary_codes == d1.primary_codes
670
743
 
744
+ # BUG-0007: same quoted_user_text across multiple answer_ref required topics fails
745
+ dup_txt = "synthetic blob echoed for every topic"
746
+ dup_rows = []
747
+ for i, key in enumerate(small):
748
+ dup_rows.append(
749
+ {
750
+ "topic_key": key,
751
+ "satisfied_by": "answer_ref",
752
+ "quoted_user_text": dup_txt,
753
+ "intake_run_id": rid,
754
+ "turn_index": 300 + i,
755
+ "ref": build_ie_ref(rid, 300 + i, key, "answer_ref", dup_txt),
756
+ }
757
+ )
758
+ dup_bundle = {
759
+ "selected_pack": "small-intake-pack",
760
+ "intake_run_id": rid,
761
+ "asked_topics": list(small),
762
+ "missing_topics": [],
763
+ "assumptions_confirmed": "(none)",
764
+ "topic_coverage": dup_rows,
765
+ }
766
+ dup_res = validate_intake_evidence(dup_bundle)
767
+ assert not dup_res.ok
768
+ assert "INTAKE_ANSWER_REF_NOT_TOPIC_DISTINCT" in dup_res.primary_codes
769
+
671
770
  # First-intake full-plan coverage contract (US-0081 / DEC-0064)
672
771
  first = PACK_REQUIRED_KEYS["first-intake-pack"]
673
772
  first_rows = []
@@ -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())
@@ -9,12 +9,30 @@ description: "its-magic auto: deterministic continuation orchestrator."
9
9
  - tech-lead
10
10
 
11
11
  ## Execution model
12
- - `/auto` is an orchestrator only. It must not execute phase work directly.
13
- - For each phase, spawn a fresh subagent context for that phase role.
12
+ - `/auto` is a **spawn-only orchestrator**: it schedules materialization, spawns
13
+ fresh **phase-role** subagents, and verifies phase boundaries—it **must not**
14
+ execute lifecycle phase work, perform phase-role duties, or author **phase
15
+ deliverables** in the orchestrator context.
16
+ - For each phase, **spawn a fresh subagent** for that phase’s canonical role;
17
+ phase output must arrive only via artifacts and handoff files (no in-turn
18
+ orchestrator execution of that phase).
14
19
  - Phase context transfer happens only through artifacts and handoff files.
15
20
  - Scope is process/workflow orchestration only. Do not claim runtime product
16
21
  orchestration changes.
17
22
 
23
+ ## Spawn-boundary integrity (BUG-0006)
24
+
25
+ - **Forbidden**: treating the orchestrator turn as the executor of a lifecycle
26
+ phase (for example running **`architecture`**, **`execute`**, **`qa`**, or any
27
+ other **`phase_id`** in the orchestrator instead of spawning the required
28
+ subagent).
29
+ - **Fail fast** with **`AUTO_ORCHESTRATOR_PHASE_EXECUTION`**. **Remediation**:
30
+ stop; spawn a **fresh** subagent for the canonical **`phase_id`** and **`role`**
31
+ per the phase→role matrix (**DEC-0051**); do not merge phase output into
32
+ orchestrator turns. **Distinct from** **`PHASE_CONTEXT_ISOLATION_VIOLATION`**
33
+ (wrong writer / isolation break) and **`RUNTIME_PROOF_*`** / **`PHASE_ROLE_*`**
34
+ families—do not overload those codes for a missing-spawn violation.
35
+
18
36
  ## Full specification (US-0080 / DEC-0062)
19
37
 
20
38
  Long prose, expanded mode semantics, and **Steps 1–13** detail live in
@@ -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.
@@ -21,6 +21,7 @@ description: "its-magic intake: clarify idea and capture story + acceptance."
21
21
  - `docs/product/backlog.md`
22
22
  - `docs/product/acceptance.md`
23
23
  - `handoffs/po_to_tl.md`
24
+ - `handoffs/resume_brief.md` (required on successful **`/intake bug`** persistence — **DEC-0069** / **BUG-0005**)
24
25
  - Optional (when enabled): `docs/engineering/compatibility-report.md`
25
26
  - Optional (when enabled): `docs/engineering/component-scope.md`
26
27
 
@@ -41,7 +42,7 @@ description: "its-magic intake: clarify idea and capture story + acceptance."
41
42
  - For artifact mutations, enforce deterministic single-writer scope:
42
43
  - establish writer identity (`writer_id`) and run identity (`intake_run_id`),
43
44
  - bind writes to target artifacts (`backlog`, `acceptance`, `vision`,
44
- `po_to_tl`) for this run.
45
+ `po_to_tl`, and when persisting bugs: `resume_brief` per **DEC-0069**) for this run.
45
46
  - Drift guard semantics:
46
47
  - self-write changes from the same `(writer_id, intake_run_id)` are valid and
47
48
  must not trigger concurrent-writer blockers,
@@ -73,6 +74,7 @@ description: "its-magic intake: clarify idea and capture story + acceptance."
73
74
  - `INTAKE_REQUIRED_TOPIC_MISSING`
74
75
  - `INTAKE_REQUIRED_PACK_INCOMPLETE`
75
76
  - `INTAKE_ASSUMPTION_CONFIRMATION_REQUIRED`
77
+ - `INTAKE_ANSWER_REF_NOT_TOPIC_DISTINCT` (**BUG-0007** / **R-0066** — see **Truthfulness** below)
76
78
  - `INTAKE_PERSISTENCE_BLOCKED`
77
79
  - Remediation guidance surface (mandatory on block):
78
80
  - list `missing_topics`,
@@ -127,6 +129,24 @@ and assumption binding. **Do not** mutate `docs/product/backlog.md` or
127
129
  (**AC-5**, **AC-6**).
128
130
  - **Grandfathering**: legacy intake rows remain valid for read/display; the **next** intake-driven
129
131
  mutation must supply full **US-0078** evidence or the write is blocked (**DEC-0060** §5).
132
+ - Truthfulness / anti-echo (**BUG-0007** / **R-0066**): `INTAKE_ANSWER_REF_NOT_TOPIC_DISTINCT` when
133
+ the same normalized `quoted_user_text` is reused across distinct required `topic_key` rows under
134
+ `satisfied_by=answer_ref` without an exempt path (`evidence_source=equivalent_evidence_ref` +
135
+ `equivalent_evidence_ref`, `delegation_ref` per **DEC-0067** / **US-0083**, or
136
+ `assumption_confirmation_ref` on the row). Canonical **`ie:`** integrity (**DEC-0060**) does not
137
+ prove a topic was actually elicited.
138
+
139
+ ## Truthfulness: `asked_topics` and `topic_coverage` (BUG-0007 / US-0083 / DEC-0060 / DEC-0067)
140
+
141
+ - **`asked_topics`** may list a required `topic_key` only when a **user-visible question** was posed
142
+ **or** a **DEC-0060**-allowed alternate applies: **`delegation_ref`** (**DEC-0067**, **US-0083**),
143
+ **`evidence_source=equivalent_evidence_ref`** with **`equivalent_evidence_ref`**, or
144
+ **`assumption_confirmation_ref`** (row-level and/or bundle-level assumption binding per the gate
145
+ contract above).
146
+ - **Forbidden**: fabricating **`topic_coverage`** by echoing **one** user or bug-report blob into
147
+ **`quoted_user_text`** on **every** required key as **`answer_ref`** solely to satisfy structure.
148
+ The validator rejects that pattern under **`INTAKE_ANSWER_REF_NOT_TOPIC_DISTINCT`** (see
149
+ **`docs/engineering/architecture.md`** **`# BUG-0007`**).
130
150
 
131
151
  ## Bug issue routing (US-0079 / DEC-0061)
132
152
 
@@ -138,6 +158,12 @@ and assumption binding. **Do not** mutate `docs/product/backlog.md` or
138
158
  - Append **`### BUG-#### — Title`** under **`## Bug issues (canonical)`** with **Status**, **`environment`**, **`steps_to_reproduce`**, **`expected`**, **`actual`**, **`evidence_refs`**.
139
159
  - Add matching **`- [ ]` / `- [x]`** row under **`## Bug acceptance (canonical)`**, sorted by id.
140
160
  - Run **`python scripts/bug_issue_validate.py --backlog docs/product/backlog.md --check-acceptance`** before completing the handoff.
161
+ - **Resume brief refresh (DEC-0069 / BUG-0005)**: immediately after successful bug persistence and backlog/acceptance validation **PASS**, run the atomic writer (temp file + replace — idempotent latest-pointer upsert):
162
+ - `python scripts/intake_bug_resume_brief_refresh.py --bug-id BUG-#### --backlog docs/product/backlog.md --resume-brief handoffs/resume_brief.md --intake-boundary-utc <RFC3339Z>`
163
+ - Optional: `--orchestrator-run-id`, `--intake-evidence handoffs/intake_evidence/....json`, `--sprint-id` when known.
164
+ - Exit non-zero → **`INTAKE_RESUME_BRIEF_*`** family — do not claim intake complete; fix backlog/brief contradiction or supply valid boundary UTC.
165
+ - Post-condition: **`intended_resume_phase` / `resolved_start_phase` = `discovery`**, **`resolution_source=resume_brief`**, **`bug_id`** matches persisted row, so **`/auto`** without **`start-from`** does not false-trigger **`RESUME_BRIEF_STALE`** for a stale pre-intake **`intake`** target.
166
+ - Optional audit: `python scripts/intake_bug_resume_brief_refresh.py --bug-id BUG-#### --backlog docs/product/backlog.md --resume-brief handoffs/resume_brief.md --validate-file` (no write).
141
167
 
142
168
  ## Steps
143
169
  1. Determine intake mode from `.cursor/scratchpad.md`:
@@ -283,7 +309,8 @@ and assumption binding. **Do not** mutate `docs/product/backlog.md` or
283
309
  - Intake mutations must also comply with
284
310
  `docs/engineering/artifact-ownership-policy.md`.
285
311
  - Intake may mutate only intake-owned scopes (`vision`, `backlog`, `acceptance`,
286
- `po_to_tl`) for target story context.
312
+ `po_to_tl`, and **`handoffs/resume_brief.md`** only via the **DEC-0069** bug-intake
313
+ completion path / `intake_bug_resume_brief_refresh.py`) for target story context.
287
314
  - Any attempted delete/rewrite of non-intake-owned sections fails closed with
288
315
  `PHASE_OWNERSHIP_VIOLATION`.
289
316
  - If an override-authorized path is configured for an artifact but required
@@ -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`.
@@ -101,9 +101,13 @@ SPRINT_BULK_MAX_STORIES=5
101
101
  SPRINT_BULK_MAX_SPRINTS=3
102
102
  SPRINT_BULK_SELECTION=priority_then_backlog_order
103
103
  #
104
- # Remote execution
105
- # - REMOTE_EXECUTION: 0|1
106
- # - REMOTE_CONFIG: path to remote config
104
+ # Remote execution (US-0084 / US-0064)
105
+ # - REMOTE_EXECUTION: 0|1 — 0 skips remote.json validation (zero overhead; DEC-0070).
106
+ # - REMOTE_CONFIG: path to dev/Cursor remote JSON (default .cursor/remote.json).
107
+ # Release/QA SSH/Docker connectivity fields live in docs/engineering/release-targets.json
108
+ # (ssh-server, dockerOverSsh); map WSL vs SSH vs Docker-over-SSH in
109
+ # docs/engineering/runtime-connectivity.md and docs/engineering/us-0084-remote-e2e.md.
110
+ # - Summary helper (names-only stdout): python scripts/remote_config_summary.py
107
111
  REMOTE_EXECUTION=0
108
112
  REMOTE_CONFIG=.cursor/remote.json
109
113
  #
@@ -23,7 +23,7 @@ artifacts. Ordering policy and ownership policy are complementary:
23
23
  | `handoffs/release_queue.md` | target sprint row only | `release` | none |
24
24
  | `handoffs/release_notes.md` | latest pointer section | `release`, `refresh-context` | none |
25
25
  | `docs/engineering/state.md` | append-bottom checkpoints only | all delivery phases | none |
26
- | `handoffs/resume_brief.md` | current status/next-actions sections | `pause`, `resume`, `refresh-context`, `release` | none |
26
+ | `handoffs/resume_brief.md` | current status/next-actions sections; latest-pointer upsert on **`/intake bug`** completion (**DEC-0069**) | `intake` (bug persistence path only), `pause`, `resume`, `refresh-context`, `release` | none |
27
27
 
28
28
  ## Non-destructive mutation rules
29
29
 
@@ -28,6 +28,8 @@ scripts/intake_evidence_lib.py
28
28
  scripts/intake_bug_routing_guard.py
29
29
  scripts/check_intake_template_parity.py
30
30
  scripts/materialize_codebase_map.py
31
+ scripts/remote_config_summary.py
32
+ scripts/guard_installer_publish.py
31
33
  scripts/enforce-triad-hot-surface.py
32
34
  .github/workflows
33
35
  README.md
@@ -52,6 +54,8 @@ scripts/intake_evidence_lib.py
52
54
  scripts/intake_bug_routing_guard.py
53
55
  scripts/check_intake_template_parity.py
54
56
  scripts/materialize_codebase_map.py
57
+ scripts/remote_config_summary.py
58
+ scripts/guard_installer_publish.py
55
59
  scripts/enforce-triad-hot-surface.py
56
60
  .github/workflows/ci.yml
57
61
  .github/workflows/deploy.yml
@@ -69,4 +73,6 @@ scripts/intake_evidence_lib.py
69
73
  scripts/intake_bug_routing_guard.py
70
74
  scripts/check_intake_template_parity.py
71
75
  scripts/materialize_codebase_map.py
76
+ scripts/remote_config_summary.py
77
+ scripts/guard_installer_publish.py
72
78
  scripts/enforce-triad-hot-surface.py