@rashidee/co2 1.3.17 → 1.3.19

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.
Files changed (68) hide show
  1. package/dist/index.js +75 -26
  2. package/package.json +1 -1
  3. package/plugin/.claude-plugin/marketplace.json +1 -1
  4. package/plugin/.claude-plugin/plugin.json +1 -1
  5. package/plugin/skills/util-gencicdscript/SKILL.md +18 -23
  6. package/plugin/skills/util-gencicdscript/references/cicd-app-template.md +176 -141
  7. package/plugin/skills/util-plancicd/SKILL.md +319 -312
  8. package/static/assets/{abnfDiagram-VRR7QNED-kjS3baPt.js → abnfDiagram-VRR7QNED-cQAiKjYG.js} +1 -1
  9. package/static/assets/{arc-CXd0xnDj.js → arc-C4ZlEQ3S.js} +1 -1
  10. package/static/assets/{architectureDiagram-ZJ3FMSHR-5QfnAz_Q.js → architectureDiagram-ZJ3FMSHR-C_31NGnA.js} +1 -1
  11. package/static/assets/{blockDiagram-677ZJIJ3-ByICFNDb.js → blockDiagram-677ZJIJ3-DDQQ2PAJ.js} +1 -1
  12. package/static/assets/{c4Diagram-LMCZKHZV-C5a0Ubby.js → c4Diagram-LMCZKHZV-BPbCZxQJ.js} +1 -1
  13. package/static/assets/channel-CufY_jLp.js +1 -0
  14. package/static/assets/{chunk-2Q5K7J3B-UucADWxr.js → chunk-2Q5K7J3B-B3LoONkc.js} +1 -1
  15. package/static/assets/{chunk-32BRIVSS-WAe17d3N.js → chunk-32BRIVSS-Buim75a9.js} +1 -1
  16. package/static/assets/{chunk-5VM5RSS4-BQBMOb0f.js → chunk-5VM5RSS4-BjHqxxbw.js} +1 -1
  17. package/static/assets/{chunk-EX3LRPZG-BzaET0n4.js → chunk-EX3LRPZG-Dn0_qUTW.js} +1 -1
  18. package/static/assets/{chunk-JWPE2WC7-D_tBvbZg.js → chunk-JWPE2WC7-DpYEF0BV.js} +1 -1
  19. package/static/assets/{chunk-MOJQB5TN-CsYb9JBc.js → chunk-MOJQB5TN-CbNDWijQ.js} +1 -1
  20. package/static/assets/{chunk-RYQCIY6F-D5xRlnc7.js → chunk-RYQCIY6F-DOhXYkGG.js} +1 -1
  21. package/static/assets/{chunk-V7JOEXUC-y0cEgq6c.js → chunk-V7JOEXUC-CfASUuSJ.js} +1 -1
  22. package/static/assets/{chunk-VR4S4FIN-CSXJHPXT.js → chunk-VR4S4FIN-vgVNnFtp.js} +1 -1
  23. package/static/assets/{chunk-XXDRQBXY-QFNFGLYZ.js → chunk-XXDRQBXY-CVjoYUTB.js} +1 -1
  24. package/static/assets/classDiagram-OUVF2IWQ-CVQD_QZa.js +1 -0
  25. package/static/assets/classDiagram-v2-EOCWNBFH-CVQD_QZa.js +1 -0
  26. package/static/assets/{cose-bilkent-JH36ORCC-ValIntpZ.js → cose-bilkent-JH36ORCC-EqjuuBpY.js} +1 -1
  27. package/static/assets/{cynefin-VYW2F7L2-BgHVKyAR.js → cynefin-VYW2F7L2-DioyO_Cm.js} +1 -1
  28. package/static/assets/{cynefinDiagram-TSTJHNR4-LIZzpkLM.js → cynefinDiagram-TSTJHNR4-CuPa5A8w.js} +1 -1
  29. package/static/assets/{dagre-VKFMJZFB-DY9b36A5.js → dagre-VKFMJZFB-MIf1Dtbk.js} +1 -1
  30. package/static/assets/{diagram-FQU43EPY-aeuYUUxe.js → diagram-FQU43EPY-5OBJqg6R.js} +1 -1
  31. package/static/assets/{diagram-G47NLZAW-BWwcYYCF.js → diagram-G47NLZAW-B2ZGPKp3.js} +1 -1
  32. package/static/assets/{diagram-NH7WQ7WH-BObFlRPR.js → diagram-NH7WQ7WH-B5YEN-B-.js} +1 -1
  33. package/static/assets/{diagram-OA4YK3LP-BUnc8m1-.js → diagram-OA4YK3LP-BXg_CQ2m.js} +1 -1
  34. package/static/assets/{diagram-WEI45ONY-DsNxBIuJ.js → diagram-WEI45ONY-DVbqehF6.js} +1 -1
  35. package/static/assets/{ebnfDiagram-CCIWWBDH-DDL2Id1r.js → ebnfDiagram-CCIWWBDH-hmnXWJuZ.js} +1 -1
  36. package/static/assets/{erDiagram-Q63AITRT-DZZcG2QU.js → erDiagram-Q63AITRT-CLYATrvi.js} +1 -1
  37. package/static/assets/{flowDiagram-23GEKE2U-C6GvkBFb.js → flowDiagram-23GEKE2U-cs1YvuVH.js} +1 -1
  38. package/static/assets/{ganttDiagram-NO4QXBWP-C7ydCTnF.js → ganttDiagram-NO4QXBWP-CH5rfioY.js} +1 -1
  39. package/static/assets/{gitGraphDiagram-IHSO6WYX-C60P6bwf.js → gitGraphDiagram-IHSO6WYX-DjAOB5L7.js} +1 -1
  40. package/static/assets/{index-CfGMfAGO.js → index-B4Y8oBUt.js} +4 -4
  41. package/static/assets/{infoDiagram-FWYZ7A6U-CnH7Q2nX.js → infoDiagram-FWYZ7A6U-De2-PVxo.js} +1 -1
  42. package/static/assets/{ishikawaDiagram-FXEZZL3T-BNEvOQYX.js → ishikawaDiagram-FXEZZL3T-CDikxFR6.js} +1 -1
  43. package/static/assets/{journeyDiagram-5HDEW3XC-BOwGZTfX.js → journeyDiagram-5HDEW3XC-Bo6isp1w.js} +1 -1
  44. package/static/assets/{kanban-definition-HUTT4EX6-BEfHVl-l.js → kanban-definition-HUTT4EX6-jM1azdC7.js} +1 -1
  45. package/static/assets/{linear-BOHPNxBi.js → linear-CUk0_yzf.js} +1 -1
  46. package/static/assets/{mindmap-definition-LN4V7U3C-B84wfT_w.js → mindmap-definition-LN4V7U3C-l-cgg6PS.js} +1 -1
  47. package/static/assets/{pegDiagram-2B236MQR-BgEa7yRL.js → pegDiagram-2B236MQR-B-8nGU3G.js} +1 -1
  48. package/static/assets/{pieDiagram-ENE6RG2P-DgA9LQlc.js → pieDiagram-ENE6RG2P-Di1V86Ag.js} +1 -1
  49. package/static/assets/{quadrantDiagram-ABIIQ3AL-C5K_r0Mx.js → quadrantDiagram-ABIIQ3AL-BknVxapP.js} +1 -1
  50. package/static/assets/{railroadDiagram-RFXS5EU6-Ckh_mLXW.js → railroadDiagram-RFXS5EU6-D7Vohrlg.js} +1 -1
  51. package/static/assets/{requirementDiagram-TGXJPOKE-CtII7Obm.js → requirementDiagram-TGXJPOKE-BlFXj6Li.js} +1 -1
  52. package/static/assets/{sankeyDiagram-HTMAVEWB-DdkySwbL.js → sankeyDiagram-HTMAVEWB-Ck_A1wxA.js} +1 -1
  53. package/static/assets/{sequenceDiagram-DBY2YBRQ-DEIBg3kY.js → sequenceDiagram-DBY2YBRQ-D4mu07Ol.js} +1 -1
  54. package/static/assets/{sizeCapture-X5ZJPWSS-D2MM9f6M.js → sizeCapture-X5ZJPWSS-BwempBoZ.js} +1 -1
  55. package/static/assets/{stateDiagram-2N3HPSRC-DHmJsAte.js → stateDiagram-2N3HPSRC-DSh1YMwZ.js} +1 -1
  56. package/static/assets/stateDiagram-v2-6OUMAXLB-DrMkFhr2.js +1 -0
  57. package/static/assets/{swimlanes-5IMT3BWC-BBPvUQvZ.js → swimlanes-5IMT3BWC-4EE1KLA5.js} +2 -2
  58. package/static/assets/swimlanesDiagram-G3AALYLV-DoZQ3XaY.js +8 -0
  59. package/static/assets/{timeline-definition-FHXFAJF6-CG_Q0YTD.js → timeline-definition-FHXFAJF6-DVZepywq.js} +1 -1
  60. package/static/assets/{vennDiagram-L72KCM5P-DlFSe4is.js → vennDiagram-L72KCM5P-YqnYvNJT.js} +1 -1
  61. package/static/assets/{wardleyDiagram-EHGQE667-BTlvtp2f.js → wardleyDiagram-EHGQE667-Dc6PvPZE.js} +1 -1
  62. package/static/assets/{xychartDiagram-FW5EYKEG-Bm14zLxj.js → xychartDiagram-FW5EYKEG-DLbY5Kg1.js} +1 -1
  63. package/static/index.html +1 -1
  64. package/static/assets/channel-CrYjYK1f.js +0 -1
  65. package/static/assets/classDiagram-OUVF2IWQ-DoLCCqXD.js +0 -1
  66. package/static/assets/classDiagram-v2-EOCWNBFH-DoLCCqXD.js +0 -1
  67. package/static/assets/stateDiagram-v2-6OUMAXLB-B6sumoG6.js +0 -1
  68. package/static/assets/swimlanesDiagram-G3AALYLV-DH2ts1oT.js +0 -8
package/dist/index.js CHANGED
@@ -6809,7 +6809,7 @@ var {
6809
6809
  // package.json
6810
6810
  var package_default = {
6811
6811
  name: "@rashidee/co2",
6812
- version: "1.3.17",
6812
+ version: "1.3.19",
6813
6813
  description: "Compound Context Studio \u2014 self-hosted team context authoring for CO2 projects",
6814
6814
  type: "module",
6815
6815
  license: "MIT",
@@ -21875,6 +21875,7 @@ import { spawn as spawn2 } from "child_process";
21875
21875
  import { randomBytes as randomBytes2 } from "crypto";
21876
21876
  import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
21877
21877
  import { request as httpRequest2 } from "http";
21878
+ import { createServer as createNetServer } from "net";
21878
21879
  import { join as join9 } from "path";
21879
21880
  function svcError6(code, status) {
21880
21881
  return Object.assign(new Error(code), { code, status });
@@ -21896,8 +21897,22 @@ process.once("SIGINT", () => {
21896
21897
  killSpawnedApps();
21897
21898
  process.exit(130);
21898
21899
  });
21899
- function cicdGuardTokenValid(projectId, token) {
21900
- return !!token && guardTokens.get(projectId) === token;
21900
+ function persistGuardToken(db, projectId, token) {
21901
+ try {
21902
+ db.update(cicdSetups).set({ guardToken: token, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(cicdSetups.projectId, projectId)).run();
21903
+ } catch {
21904
+ }
21905
+ }
21906
+ function cicdGuardTokenValid(db, projectId, token) {
21907
+ if (!token) return false;
21908
+ const mem = guardTokens.get(projectId);
21909
+ if (mem !== void 0) return mem === token;
21910
+ const row = db.select({ t: cicdSetups.guardToken }).from(cicdSetups).where(eq(cicdSetups.projectId, projectId)).get();
21911
+ if (row?.t) {
21912
+ guardTokens.set(projectId, row.t);
21913
+ return row.t === token;
21914
+ }
21915
+ return false;
21901
21916
  }
21902
21917
  function resolveCicdApp(db, workspacePath, projectId) {
21903
21918
  const project = db.select().from(projects).where(eq(projects.id, projectId)).get();
@@ -21930,6 +21945,49 @@ function probeApp(port, timeoutMs = 1e3) {
21930
21945
  req.end();
21931
21946
  });
21932
21947
  }
21948
+ function portFree(port) {
21949
+ return new Promise((resolve5) => {
21950
+ const srv = createNetServer();
21951
+ srv.once("error", () => resolve5(false));
21952
+ srv.once("listening", () => srv.close(() => resolve5(true)));
21953
+ srv.listen(port, "127.0.0.1");
21954
+ });
21955
+ }
21956
+ function ephemeralPort() {
21957
+ return new Promise((resolve5) => {
21958
+ const srv = createNetServer();
21959
+ srv.once("error", () => resolve5(0));
21960
+ srv.listen(0, "127.0.0.1", () => {
21961
+ const addr = srv.address();
21962
+ const p = addr && typeof addr === "object" ? addr.port : 0;
21963
+ srv.close(() => resolve5(p));
21964
+ });
21965
+ });
21966
+ }
21967
+ async function pickFreePort(preferred, avoid) {
21968
+ if (preferred && preferred !== avoid && await portFree(preferred)) return preferred;
21969
+ for (let i = 0; i < 20; i++) {
21970
+ const p = await ephemeralPort();
21971
+ if (p && p !== avoid) return p;
21972
+ }
21973
+ return preferred || DEFAULT_CICD_APP_PORT;
21974
+ }
21975
+ function persistConfigPort(location, newPort) {
21976
+ if (newPort === location.port) return;
21977
+ const configPath = join9(location.appDir, "cicd.config.json");
21978
+ let config = {};
21979
+ try {
21980
+ config = JSON.parse(readFileSync6(configPath, "utf8"));
21981
+ } catch {
21982
+ }
21983
+ config.port = newPort;
21984
+ try {
21985
+ writeFileSync5(configPath, `${JSON.stringify(config, null, 2)}
21986
+ `);
21987
+ } catch {
21988
+ }
21989
+ location.port = newPort;
21990
+ }
21933
21991
  function toStatus2(location, running, projectId) {
21934
21992
  return {
21935
21993
  present: location.present,
@@ -21948,26 +22006,13 @@ async function getCicdAppStatus(db, workspacePath, projectId) {
21948
22006
  async function startCicdApp(db, workspacePath, projectId, studioPort, port) {
21949
22007
  const location = resolveCicdApp(db, workspacePath, projectId);
21950
22008
  if (!location.present) throw svcError6("CICD_APP_MISSING", 404);
21951
- const targetPort = port ?? location.port;
21952
- if (targetPort !== location.port) {
21953
- const configPath = join9(location.appDir, "cicd.config.json");
21954
- let config = {};
21955
- try {
21956
- config = JSON.parse(readFileSync6(configPath, "utf8"));
21957
- } catch {
21958
- }
21959
- config.port = targetPort;
21960
- try {
21961
- writeFileSync5(configPath, `${JSON.stringify(config, null, 2)}
21962
- `);
21963
- } catch {
21964
- }
21965
- location.port = targetPort;
21966
- }
21967
- if (await probeApp(targetPort)) {
21968
- location.port = targetPort;
22009
+ const savedPort = port ?? location.port;
22010
+ if (await probeApp(savedPort)) {
22011
+ persistConfigPort(location, savedPort);
21969
22012
  return toStatus2(location, true, projectId);
21970
22013
  }
22014
+ const targetPort = port !== void 0 ? port : await pickFreePort(savedPort, studioPort);
22015
+ persistConfigPort(location, targetPort);
21971
22016
  const existing = spawnedApps.get(projectId);
21972
22017
  if (existing && existing.exitCode === null) {
21973
22018
  try {
@@ -21993,20 +22038,22 @@ async function startCicdApp(db, workspacePath, projectId, studioPort, port) {
21993
22038
  child.on("error", () => {
21994
22039
  spawnedApps.delete(projectId);
21995
22040
  guardTokens.delete(projectId);
22041
+ persistGuardToken(db, projectId, null);
21996
22042
  });
21997
22043
  child.on("exit", () => {
21998
22044
  spawnedApps.delete(projectId);
21999
22045
  guardTokens.delete(projectId);
22046
+ persistGuardToken(db, projectId, null);
22000
22047
  });
22001
22048
  spawnedApps.set(projectId, child);
22002
- db.update(cicdSetups).set({ appPort: location.port, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(cicdSetups.projectId, projectId)).run();
22049
+ db.update(cicdSetups).set({ appPort: location.port, guardToken: token, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(cicdSetups.projectId, projectId)).run();
22003
22050
  for (let attempt = 0; attempt < 20; attempt++) {
22004
22051
  if (await probeApp(location.port, 500)) return toStatus2(location, true, projectId);
22005
22052
  await new Promise((r) => setTimeout(r, 250));
22006
22053
  }
22007
22054
  return toStatus2(location, false, projectId);
22008
22055
  }
22009
- function stopCicdApp(projectId) {
22056
+ function stopCicdApp(db, projectId) {
22010
22057
  const child = spawnedApps.get(projectId);
22011
22058
  if (!child) return false;
22012
22059
  try {
@@ -22015,6 +22062,7 @@ function stopCicdApp(projectId) {
22015
22062
  }
22016
22063
  spawnedApps.delete(projectId);
22017
22064
  guardTokens.delete(projectId);
22065
+ persistGuardToken(db, projectId, null);
22018
22066
  return true;
22019
22067
  }
22020
22068
 
@@ -22701,6 +22749,7 @@ function pollCicdTracking(db, workspacePath, setupId, kind, notifyUserId) {
22701
22749
  relatedEntityType: "cicd_setup",
22702
22750
  relatedEntityId: setupId
22703
22751
  });
22752
+ stopCicdApp(db, setup.projectId);
22704
22753
  try {
22705
22754
  runProjectGit(
22706
22755
  projectDir2,
@@ -22854,7 +22903,7 @@ function cicdRoutes(db, workspacePath, studioPort) {
22854
22903
  const user = c.get("user");
22855
22904
  try {
22856
22905
  const result = requestPlan(db, workspacePath, projectId, user.id);
22857
- stopCicdApp(projectId);
22906
+ stopCicdApp(db, projectId);
22858
22907
  return c.json(result, 201);
22859
22908
  } catch (e) {
22860
22909
  return errorResponse3(c, e);
@@ -22910,7 +22959,7 @@ function cicdRoutes(db, workspacePath, studioPort) {
22910
22959
  const projectId = parseProjectId(c);
22911
22960
  if (projectId === null) return c.json({ code: "INVALID_ID" }, 400);
22912
22961
  try {
22913
- stopCicdApp(projectId);
22962
+ stopCicdApp(db, projectId);
22914
22963
  return c.json(await getCicdAppStatus(db, workspacePath, projectId));
22915
22964
  } catch (e) {
22916
22965
  return errorResponse3(c, e);
@@ -22943,7 +22992,7 @@ function cicdGuardRoutes(db, workspacePath) {
22943
22992
  if (Number.isNaN(projectId)) return c.json({ code: "INVALID_ID" }, 400);
22944
22993
  const bearer = c.req.header("authorization")?.replace(/^Bearer\s+/i, "");
22945
22994
  const token = c.req.query("token") ?? bearer;
22946
- if (!cicdGuardTokenValid(projectId, token) && !c.get("user")) {
22995
+ if (!cicdGuardTokenValid(db, projectId, token) && !c.get("user")) {
22947
22996
  return c.json({ code: "UNAUTHORIZED" }, 401);
22948
22997
  }
22949
22998
  const rawVersion = c.req.query("versionId");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rashidee/co2",
3
- "version": "1.3.17",
3
+ "version": "1.3.19",
4
4
  "description": "Compound Context Studio — self-hosted team context authoring for CO2 projects",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -12,7 +12,7 @@
12
12
  "name": "co2-skills",
13
13
  "source": "./",
14
14
  "description": "Compound Context Skills — generates data models, HTML mockups, technical specifications, test specifications, and orchestrates full-stack development from Agile user stories.",
15
- "version": "1.7.0",
15
+ "version": "1.8.2",
16
16
  "author": {
17
17
  "name": "Rashidee M.Rashid"
18
18
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "co2-skills",
3
- "version": "1.7.0",
3
+ "version": "1.8.2",
4
4
  "description": "Compound Context Skills — an opinionated context engineering methodology for AI-driven software development. Generates data models, HTML mockups, technical specifications, test specifications, and orchestrates full-stack development from Agile user stories.",
5
5
  "author": {
6
6
  "name": "Rashidee M.Rashid"
@@ -21,12 +21,12 @@ description: >
21
21
  copy, docker run or kubectl apply, or none). Controls are disabled for any application that
22
22
  has never completed a conductor-develop workflow run (checked against the studio's
23
23
  /api/cicd-guard endpoint using the spawn-env token). Health/port probing marks apps already
24
- running — even ones started outside this app. There is NO version selector every control
25
- operates on the current working tree, and the {VERSION} token in commands auto-resolves to
26
- the project's current version (studio guard's latest semver, fallback `latest`) docker
27
- images are tagged <repo>/<app>:<version> when the plan's Docker section is active (managed
28
- Dockerfiles materialized before Build when missing; Build never pushes) — and Build All /
29
- Start All / Stop All buttons operate every eligible application at once (sequential builds). When the approved plan declares an `## Infrastructure` list, the
24
+ running — even ones started outside this app. There is NO version selector and there are NO
25
+ Build All / Start All / Stop All buttons every control operates per application on the
26
+ current working tree, and the plan's commands run verbatim (no {VERSION} or other token
27
+ substitution) — docker images are tagged <repo>/<app>:latest when the plan's Docker section
28
+ is active (managed Dockerfiles materialized before Build when missing; Build never
29
+ pushes). When the approved plan declares an `## Infrastructure` list, the
30
30
  main page shows an "Infrastructure" link that opens a separate status-only page listing every
31
31
  supporting 3rd-party service with a live TCP-reachability indicator and a console link. Writes cicd/CICD_GENERATION.md with `**Status**:` IN PROGRESS →
32
32
  COMPLETED so the studio watcher detects completion. Idempotent — re-running regenerates
@@ -66,18 +66,20 @@ Write `cicd/CICD_GENERATION.md`:
66
66
  Read `references/cicd-app-template.md` for the complete file templates. Produce:
67
67
 
68
68
  1. `cicd/app/cicd.config.json` — from the plan's tables:
69
- `{ "port": <CI/CD app port>, "docker": { "registry" } | null, "apps": [{ "name", "folder",
69
+ `{ "port": <CI/CD app port>, "generatedAt": "<current ISO-8601 UTC>", "docker": { "registry" } | null, "apps": [{ "name", "folder",
70
70
  "method", "port", "build", "start", "stop", "deployMethod", "deploy", "health", "image",
71
71
  "dockerfile", "configs" }], "infra": [{ "id", "name",
72
- "host", "port", "url", "description" }] }` — `docker.registry` comes from the plan's
72
+ "host", "port", "url", "description" }] }` — `generatedAt` MUST be the actual time you generate
73
+ this file (a fresh value on EVERY run — never copy a previous one): the running server pins the
74
+ value it booted with and shows a "restart required" banner when a later regeneration moves it
75
+ forward, so an in-place-replaced-but-still-running app is self-diagnosing. `docker.registry` comes from the plan's
73
76
  `## Docker` section (`null` when the plan says not dockerized); per-app `image` is that
74
77
  section's Image column (`null` for non-dockerized apps) and `dockerfile` is
75
78
  `{ "path": "Dockerfile", "content": "<body>" }` for a `managed` Dockerfile from the plan's
76
79
  `## Dockerfiles` section, or `{ "path": "Dockerfile", "content": null }` for an `existing`
77
80
  one (`null` entirely when the app has no image). Copy managed Dockerfile bodies verbatim.
78
- Commands may carry the literal `{VERSION}` tokencopy it verbatim; the generated app
79
- substitutes it at execution with the project's CURRENT version (studio guard's latest
80
- semver, `v` prefix stripped, fallback `latest`) — never via any user-facing selector. `deployMethod` is the plan's Deploy Method column
81
+ Commands are copied verbatim from the plan and run as-is there is NO `{VERSION}` token
82
+ or any other placeholder substitution. `deployMethod` is the plan's Deploy Method column
81
83
  (`none|web-server|docker|kubernetes`) and `deploy` its command (`null` when the method is
82
84
  `none`). The `infra` array comes from the plan's `## Infrastructure` table (one entry per
83
85
  row); `url` is `null` when the Console URL cell is `-`. Emit `"infra": []` when the plan's
@@ -164,22 +166,15 @@ port, application count) and STOP — the studio starts/embeds the app.
164
166
  (`<project>/<folder>`) as working directory — command paths are relative to the application
165
167
  folder (e.g. `java -jar target\app.jar`), never to the project root. Deploy renders only
166
168
  when the plan assigned a deploy method other than `none`.
167
- - **NO version selector current codebase only**: the page carries NO version dropdown or
168
- any other version choice. Build/Start/Stop/Deploy (and the All buttons) always operate on
169
- the CURRENT working tree there is nothing else they could build. The literal `{VERSION}`
170
- token in commands is resolved AUTOMATICALLY at execution time to the project's current
171
- version: the FIRST entry of the studio guard response's `versions` array (already polled
172
- every 30 s; latest first), minus its `v` prefix, falling back to `latest` when the studio
173
- is unreachable. Never render a UI control for it and never check out code.
169
+ - **NO version UI, NO All-apps buttons, NO command substitution**: the page carries NO
170
+ version dropdown or any other version choice, and NO Build All / Start All / Stop All
171
+ buttonsBuild/Start/Stop/Deploy are per-application only and always operate on the
172
+ CURRENT working tree, running the plan's commands verbatim (no `{VERSION}` or other token
173
+ substitution). Never render a version control and never check out code.
174
174
  - **Dockerfile materialization**: before Build, when an app's `dockerfile.content` is set and
175
175
  no file exists at its path in the application folder, the server writes it (never
176
176
  overwriting an existing Dockerfile — mirror of the managed-config rule). Build never
177
177
  pushes an image; pushing belongs to a `docker` deploy command.
178
- - **All-apps controls**: the top bar carries **Build All**, **Start All** and **Stop All**
179
- buttons. Build All builds eligible apps SEQUENTIALLY (one at a time — honest logs, no CPU
180
- storm); Start All starts every eligible stopped app; Stop All stops every running app.
181
- Eligible = develop-gate not failed, not busy, and (for build/start) not already running;
182
- ineligible apps are skipped silently — per-tab badges show progress.
183
178
  - Develop gate: controls stay disabled for an application that never completed a
184
179
  conductor-develop run — checked via `${CO2_STUDIO_URL}/api/cicd-guard/${CO2_PROJECT_ID}`
185
180
  with the CO2_GUARD_TOKEN from the spawn env (refreshed every 30 s, last answer cached).