hanoman 0.1.19 → 0.1.21

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/dist/server.js CHANGED
@@ -4164,6 +4164,98 @@ var init_enums = __esm({
4164
4164
  }
4165
4165
  });
4166
4166
 
4167
+ // ../shared/src/spec-source.ts
4168
+ function payloadShapeFor(source) {
4169
+ return source === "qa" ? "qa" : source === "goal" ? "goal" : "brief";
4170
+ }
4171
+ function shapeOfPayload(payload) {
4172
+ const p = payload ?? {};
4173
+ return "severity" in p ? "qa" : "goal" in p ? "goal" : "brief";
4174
+ }
4175
+ function payloadMatchesSource(source, payload) {
4176
+ return shapeOfPayload(payload) === payloadShapeFor(source);
4177
+ }
4178
+ function priorityFromSeverity(severity) {
4179
+ return severity === "minor" ? "sedang" : "tinggi";
4180
+ }
4181
+ function severityFromPriority(priority) {
4182
+ return priority === "tinggi" ? "major" : "minor";
4183
+ }
4184
+ function convertPayload(to, payload) {
4185
+ const p = payload ?? {};
4186
+ const str3 = (k) => typeof p[k] === "string" ? p[k] : "";
4187
+ const prio = () => p.priority === "tinggi" || p.priority === "rendah" ? p.priority : "sedang";
4188
+ const nonEmpty = (fields) => fields.filter((f) => str3(f) !== "");
4189
+ const fromShape = shapeOfPayload(payload);
4190
+ const toShape = payloadShapeFor(to);
4191
+ const fromAudit = str3("fromAudit");
4192
+ const done = (out3, dropped) => ({
4193
+ payload: out3,
4194
+ dropped,
4195
+ missing: SHAPE_REQUIRED[toShape].filter((f) => typeof out3[f] !== "string" || out3[f] === "")
4196
+ });
4197
+ if (fromShape === toShape) return { payload: { ...p }, dropped: [], missing: [] };
4198
+ if (toShape === "qa") {
4199
+ if (fromShape === "brief")
4200
+ return done({
4201
+ severity: severityFromPriority(prio()),
4202
+ steps: "",
4203
+ expected: str3("outcome"),
4204
+ actual: str3("context"),
4205
+ env: "",
4206
+ ...fromAudit ? { fromAudit } : {}
4207
+ }, nonEmpty(["constraints"]));
4208
+ return done({
4209
+ severity: severityFromPriority(prio()),
4210
+ steps: "",
4211
+ expected: str3("goal"),
4212
+ actual: "",
4213
+ env: ""
4214
+ }, nonEmpty(["done", "constraints"]));
4215
+ }
4216
+ if (toShape === "goal") {
4217
+ if (fromShape === "brief") {
4218
+ const goal = str3("outcome") || str3("context");
4219
+ return done(
4220
+ { goal, done: "", constraints: str3("constraints"), priority: prio() },
4221
+ nonEmpty([...str3("outcome") ? ["context"] : [], "fromAudit"])
4222
+ );
4223
+ }
4224
+ return done({
4225
+ goal: str3("expected"),
4226
+ done: "",
4227
+ constraints: "",
4228
+ priority: priorityFromSeverity(p.severity)
4229
+ }, nonEmpty(["steps", "actual", "env", "fromAudit"]));
4230
+ }
4231
+ if (fromShape === "qa")
4232
+ return done({
4233
+ context: str3("actual"),
4234
+ outcome: str3("expected"),
4235
+ constraints: "",
4236
+ priority: priorityFromSeverity(p.severity),
4237
+ ...fromAudit ? { fromAudit } : {}
4238
+ }, nonEmpty(["steps", "env"]));
4239
+ return done({
4240
+ context: "",
4241
+ outcome: str3("goal"),
4242
+ constraints: str3("constraints"),
4243
+ priority: prio()
4244
+ }, nonEmpty(["done"]));
4245
+ }
4246
+ var SHAPE_REQUIRED;
4247
+ var init_spec_source = __esm({
4248
+ "../shared/src/spec-source.ts"() {
4249
+ "use strict";
4250
+ init_enums();
4251
+ SHAPE_REQUIRED = {
4252
+ brief: ["context", "outcome"],
4253
+ qa: ["steps", "expected", "actual", "env"],
4254
+ goal: ["goal", "done"]
4255
+ };
4256
+ }
4257
+ });
4258
+
4167
4259
  // ../shared/src/agent-engine.ts
4168
4260
  var zAgentEngine;
4169
4261
  var init_agent_engine = __esm({
@@ -4341,7 +4433,7 @@ function cmpVersion(a, b) {
4341
4433
  }
4342
4434
  return 0;
4343
4435
  }
4344
- var zProject, zBriefPayload, zQaPayload, zGoalPayload, zSpecBlocker, zSpec, NOTIFY_SOUNDS, MODELS, EFFORTS, E_5_6, E_LUNA, E_BASE, CODEX_MODELS, RETIRED_CODEX_MODELS, zCodex, CODEX_DEFAULTS, zSourceCommon, zScheduler, SCHEDULER_DEFAULTS, zGoal, GOAL_DEFAULTS, zConflict, CONFLICT_DEFAULTS, CHANGELOG_ENGINE_DEFAULTS, zLeadEngine, zLead, LEAD_DEFAULTS, zSetting, zNotification, zDocFile, zDeviceTokenView;
4436
+ var zProject, zBriefPayload, zQaPayload, zGoalPayload, zSpecBlocker, zSourceChange, zSpec, NOTIFY_SOUNDS, MODELS, EFFORTS, E_5_6, E_LUNA, E_BASE, CODEX_MODELS, RETIRED_CODEX_MODELS, zCodex, CODEX_DEFAULTS, zSourceCommon, zScheduler, SCHEDULER_DEFAULTS, zGoal, GOAL_DEFAULTS, zConflict, CONFLICT_DEFAULTS, CHANGELOG_ENGINE_DEFAULTS, zLeadEngine, zLead, LEAD_DEFAULTS, zSetting, zNotification, zDocFile, zDeviceTokenView;
4345
4437
  var init_entities = __esm({
4346
4438
  "../shared/src/entities.ts"() {
4347
4439
  "use strict";
@@ -4391,6 +4483,13 @@ var init_entities = __esm({
4391
4483
  id: external_exports.string(),
4392
4484
  reason: external_exports.enum(["missing", "unfinished", "unmerged"])
4393
4485
  });
4486
+ zSourceChange = external_exports.object({
4487
+ at: external_exports.string(),
4488
+ from: external_exports.string(),
4489
+ to: external_exports.string(),
4490
+ by: external_exports.string(),
4491
+ payload: external_exports.unknown().optional()
4492
+ });
4394
4493
  zSpec = external_exports.object({
4395
4494
  id: external_exports.string(),
4396
4495
  projectId: external_exports.string(),
@@ -4418,7 +4517,11 @@ var init_entities = __esm({
4418
4517
  blockedBy: external_exports.array(zSpecBlocker).default([]),
4419
4518
  // SPEC-486 · ADR-0103 · override kebijakan auto-merge item ini; null = warisi project.
4420
4519
  // `.nullable().default(null)` menjaga respons/klien versi lama tetap parse.
4421
- autoMerge: zAutoMerge.nullable().default(null)
4520
+ autoMerge: zAutoMerge.nullable().default(null),
4521
+ // SPEC-546 · ADR-0109 · jejak konversi type. `.default([])` menjaga respons/klien versi lama
4522
+ // tetap parse; kolom DB-nya `Json?` sehingga baris yang belum pernah dikonversi mengirim
4523
+ // `null` — pemakai UI menulis `spec.sourceHistory ?? []`, cermin `blockedBy`.
4524
+ sourceHistory: external_exports.array(zSourceChange).default([])
4422
4525
  });
4423
4526
  NOTIFY_SOUNDS = [
4424
4527
  "off",
@@ -5134,7 +5237,7 @@ var init_prd_status = __esm({
5134
5237
  function flowForSource(source) {
5135
5238
  return source === "qa" ? "qa" : source === "audit" ? "audit" : source === "goal" ? "goal" : "feature";
5136
5239
  }
5137
- var zIssueDeviceToken, zSessionResult, zSessionHistory, zCreateProject, zProjectId, zRenameProject, zUpdateProject, zCreateSpec, zPatchSpec, zIntegrate, zSessionSummary, zProjectView, zSchedulerQueueItem, zSchedulerSourceView, zSchedulerSessionView, zSchedulerQueueCounts, zSchedulerState, zLeadDecisionView, zLeadFlowView, zLeadAnswer, zLeadProjectStatus, zLeadStatusView, zFlow, zPrdBrief, zPrdDoc, zBreakdownItem, zBreakdownDoc, zBatchCreateSpec, zEscalationTarget, zEscalationPrefill, zAuditEscalation, zAuditEscalationView, zTerminalSession, zDocFileContent, zDocIndexCat, zDocIndex, HOST_RE, USER_RE, zCreateVps, zPatchVps, zLogin, zSignup, zChangePassword, zVpsCheck, zMarkNa, zMarkNaBulk, zAttest, zRemediate, UPDATE_RESTART_EXIT, zUpdateApplyBody, zTicketView, zTicketAttachmentView, zTicketDetail, zTicketEditInput, zHelpInfo, zPublicTicketStatus;
5240
+ var zIssueDeviceToken, zSessionResult, zSessionHistory, zCreateProject, zProjectId, zRenameProject, zUpdateProject, zCreateSpec, zPatchSpec, zChangeSpecSource, zIntegrate, zSessionSummary, zProjectView, zSchedulerQueueItem, zSchedulerSourceView, zSchedulerSessionView, zSchedulerQueueCounts, zSchedulerState, zLeadDecisionView, zLeadFlowView, zLeadAnswer, zLeadProjectStatus, zLeadStatusView, zFlow, zPrdBrief, zPrdDoc, zBreakdownItem, zBreakdownDoc, zBatchCreateSpec, zEscalationTarget, zEscalationPrefill, zAuditEscalation, zAuditEscalationView, zTerminalSession, zDocFileContent, zDocIndexCat, zDocIndex, HOST_RE, USER_RE, zCreateVps, zPatchVps, zLogin, zSignup, zChangePassword, zVpsCheck, zMarkNa, zMarkNaBulk, zAttest, zRemediate, UPDATE_RESTART_EXIT, zUpdateApplyBody, zTicketView, zTicketAttachmentView, zTicketDetail, zTicketEditInput, zHelpInfo, zPublicTicketStatus;
5138
5241
  var init_dto = __esm({
5139
5242
  "../shared/src/dto.ts"() {
5140
5243
  "use strict";
@@ -5144,6 +5247,7 @@ var init_dto = __esm({
5144
5247
  init_auto_merge();
5145
5248
  init_prd_status();
5146
5249
  init_enums();
5250
+ init_spec_source();
5147
5251
  zIssueDeviceToken = external_exports.object({ name: external_exports.string().min(1) });
5148
5252
  zSessionResult = external_exports.object({
5149
5253
  id: external_exports.string(),
@@ -5211,9 +5315,7 @@ var init_dto = __esm({
5211
5315
  // SPEC-447 · ADR-0093 · divalidasi server (id ada / satu project / bukan diri sendiri / non-siklus).
5212
5316
  dependsOn: external_exports.array(external_exports.string()).optional()
5213
5317
  }).superRefine((o, ctx) => {
5214
- const shape = "severity" in o.payload ? "qa" : "goal" in o.payload ? "goal" : "brief";
5215
- const want = o.source === "qa" ? "qa" : o.source === "goal" ? "goal" : "brief";
5216
- if (shape !== want)
5318
+ if (!payloadMatchesSource(o.source, o.payload))
5217
5319
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["payload"], message: "bentuk payload tak cocok dengan source" });
5218
5320
  });
5219
5321
  zPatchSpec = external_exports.object({
@@ -5234,6 +5336,13 @@ var init_dto = __esm({
5234
5336
  // apa yang terjadi SESUDAH kerja, bukan konten yang sedang dikerjakan sesi hidup.
5235
5337
  autoMerge: zAutoMerge.nullable().optional()
5236
5338
  });
5339
+ zChangeSpecSource = external_exports.object({
5340
+ source: zSpecSource,
5341
+ payload: external_exports.union([zBriefPayload, zQaPayload, zGoalPayload]).optional()
5342
+ }).superRefine((o, ctx) => {
5343
+ if (o.payload !== void 0 && !payloadMatchesSource(o.source, o.payload))
5344
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["payload"], message: "bentuk payload tak cocok dengan source" });
5345
+ });
5237
5346
  zIntegrate = external_exports.object({
5238
5347
  op: external_exports.enum(["merge", "rebase"]),
5239
5348
  target: external_exports.string().regex(/^(local|origin):.+/)
@@ -5631,6 +5740,8 @@ var init_api = __esm({
5631
5740
  // SPEC-340 · ADR-0076 · rekomendasi tindak lanjut audit (turunan blok json dokumen audit).
5632
5741
  specEscalation: (id) => `${API}/specs/${id}/escalation`,
5633
5742
  specIntegrate: (id) => `${API}/specs/${id}/integrate`,
5743
+ // SPEC-546 · ADR-0109 · ubah type/source item in-place (operasi khusus, bukan field PATCH).
5744
+ specSource: (id) => `${API}/specs/${id}/source`,
5634
5745
  specReview: (id) => `${API}/specs/${id}/review`,
5635
5746
  specReviewFile: (id, path) => `${API}/specs/${id}/review/${path}`,
5636
5747
  settings: `${API}/settings`,
@@ -6311,6 +6422,14 @@ var init_webhook = __esm({
6311
6422
  label: "Stage backlog berpindah",
6312
6423
  changed: ["stage"],
6313
6424
  when: "Stage berpindah \u2014 baik oleh fase sesi yang tercatat (otomatis) maupun revert manual operator. Menggantikan spec.updated untuk perubahan itu."
6425
+ }, {
6426
+ // SPEC-546 · ADR-0109 · konversi type item. Pola yang sama dengan stage_changed: peristiwa
6427
+ // turunan MENGGANTIKAN spec.updated, supaya penerima bisa bereaksi pada "type berpindah"
6428
+ // tanpa mendiff dua amplop.
6429
+ type: "spec.source_changed",
6430
+ label: "Type backlog berpindah",
6431
+ changed: ["source"],
6432
+ when: "Type/source item backlog dikonversi lewat POST /specs/:id/source (mis. brief \u2192 qa). Menggantikan spec.updated untuk perubahan itu."
6314
6433
  }],
6315
6434
  sample: {
6316
6435
  id: "SPEC-481",
@@ -7250,6 +7369,7 @@ var init_src = __esm({
7250
7369
  "../shared/src/index.ts"() {
7251
7370
  "use strict";
7252
7371
  init_enums();
7372
+ init_spec_source();
7253
7373
  init_agent_engine();
7254
7374
  init_entities();
7255
7375
  init_agent();
@@ -7466,6 +7586,25 @@ var init_verify_scope = __esm({
7466
7586
  }
7467
7587
  });
7468
7588
 
7589
+ // ../runner/src/code-style.ts
7590
+ var CODE_STYLE_CLAUSE;
7591
+ var init_code_style = __esm({
7592
+ "../runner/src/code-style.ts"() {
7593
+ "use strict";
7594
+ CODE_STYLE_CLAUSE = [
7595
+ "Gaya kode \u2014 berlaku setiap kali kamu menulis atau mengubah kode:",
7596
+ "- Tulis kode yang rapi dan mengikuti idiom, penamaan, serta struktur kode di sekitarnya.",
7597
+ " Kodemu harus terbaca seperti kode yang sudah ada di berkas itu, bukan seperti tempelan.",
7598
+ "- Jangan menulis komentar yang cuma mengulang apa yang sudah dinyatakan kode.",
7599
+ "- Komentar hanya untuk hal yang TIDAK terbaca dari kode: alasan/why sebuah keputusan,",
7600
+ " trade-off yang diambil, workaround beserta rujukan SPEC/ADR-nya, atau invariant yang tak",
7601
+ " kelihatan. Komentar semacam itu justru berharga \u2014 jangan ikut dibuang.",
7602
+ "- Jangan menambahkan komentar pembatas seksi, header berhiasan, atau narasi langkah demi langkah.",
7603
+ "- Jangan meninggalkan kode mati atau kode yang dikomentari. Hapus saja; riwayat git yang menyimpannya."
7604
+ ].join("\n");
7605
+ }
7606
+ });
7607
+
7469
7608
  // ../runner/src/goal-spec.ts
7470
7609
  function readGoalPayload(payload) {
7471
7610
  if (!payload || typeof payload !== "object" || Array.isArray(payload)) return null;
@@ -7494,6 +7633,7 @@ Detail: ${JSON.stringify(spec.payload)}` : "";
7494
7633
  auditOnlyInstruction(flow),
7495
7634
  autonomyClause(autonomy),
7496
7635
  scopeClause(flow, verifyScope),
7636
+ codeStyleClause(flow),
7497
7637
  skillInstruction(PIPELINES[flow]),
7498
7638
  `Setelah fase terakhir: commit, lalu \`git push origin HEAD:refs/heads/${branchTo}\`. Worktree ini detached HEAD \u2014 itu memang disengaja.`,
7499
7639
  `Backlog item ${spec.id} \xB7 sumber ${spec.source} \xB7 prioritas ${spec.priority}
@@ -7509,6 +7649,7 @@ Detail: ${JSON.stringify(spec.payload)}` : "";
7509
7649
  `JANGAN mengulang fase awal \u2014 spec & plan sudah ada. Lanjut di fase Execute: baca plan di docs/superpowers/plans/** untuk backlog item ini, periksa task yang sudah \`[x]\` dan selesaikan yang masih \`[ ]\`. Verifikasi nyata sebelum klaim selesai.`,
7510
7650
  autonomyClause(autonomy),
7511
7651
  scopeClause(flow, verifyScope),
7652
+ codeStyleClause(flow),
7512
7653
  skillInstruction(["Execute"]),
7513
7654
  `Setelah selesai: commit, lalu \`git push origin HEAD:refs/heads/${branchTo}\`. Worktree ini detached HEAD \u2014 itu memang disengaja.`,
7514
7655
  `Backlog item ${spec.id} \xB7 sumber ${spec.source} \xB7 prioritas ${spec.priority}
@@ -7527,6 +7668,7 @@ Detail: ${JSON.stringify(spec.payload)}` : "";
7527
7668
  auditDecided ? "" : auditDecisionInstruction(flow),
7528
7669
  autonomyClause(autonomy),
7529
7670
  scopeClause(flow, verifyScope),
7671
+ codeStyleClause(flow),
7530
7672
  skillInstruction(PIPELINES[flow]),
7531
7673
  `Setelah fase terakhir: commit, lalu \`git push origin HEAD:refs/heads/${branchTo}\`. Worktree ini detached HEAD \u2014 itu memang disengaja.`,
7532
7674
  `Backlog item ${spec.id} \xB7 sumber ${spec.source} \xB7 prioritas ${spec.priority}
@@ -7549,6 +7691,7 @@ function startGoalPrompt(spec, branchTo, opts = {}) {
7549
7691
  "Fase Verifikasi bukan formalitas: jalankan perintah yang membuktikan goal-nya tercapai (test/typecheck/benchmark/perintah yang relevan) dan baca outputnya. Klaim tanpa output bukan bukti.",
7550
7692
  autonomyClause(opts.autonomy),
7551
7693
  scopeClause("goal", opts.verifyScope),
7694
+ codeStyleClause("goal"),
7552
7695
  skillInstruction(PIPELINES.goal),
7553
7696
  `Setelah fase terakhir: commit, lalu \`git push origin HEAD:refs/heads/${branchTo}\`. Worktree ini detached HEAD \u2014 itu memang disengaja.`,
7554
7697
  `Backlog item ${spec.id} \xB7 sumber ${spec.source} \xB7 prioritas ${spec.priority}
@@ -7621,12 +7764,13 @@ Stack: ${project.stack || "\u2014"}`,
7621
7764
  ${REVERSE_STANDARD}`
7622
7765
  ].filter(Boolean).join("\n\n");
7623
7766
  }
7624
- var PIPELINES, AUTONOMY_CLAUSE, AUTONOMY_CLAUSE_FULL, autonomyClause, phaseInstruction, PHASE_SKILLS, skillInstruction, auditDecisionInstruction, ESCALATION_CONTRACT, auditOnlyInstruction, auditContinuationInstruction, writesCode, scopeClause, resumeClause, RESUMED_WORKTREE_NOTE, REVERSE_PHASE_GUIDE, SCAFFOLD_PHASE_GUIDE;
7767
+ var PIPELINES, AUTONOMY_CLAUSE, AUTONOMY_CLAUSE_FULL, autonomyClause, phaseInstruction, PHASE_SKILLS, skillInstruction, auditDecisionInstruction, ESCALATION_CONTRACT, auditOnlyInstruction, auditContinuationInstruction, writesCode, scopeClause, codeStyleClause, resumeClause, RESUMED_WORKTREE_NOTE, REVERSE_PHASE_GUIDE, SCAFFOLD_PHASE_GUIDE;
7625
7768
  var init_prompt = __esm({
7626
7769
  "../runner/src/prompt.ts"() {
7627
7770
  "use strict";
7628
7771
  init_reverse_standard();
7629
7772
  init_verify_scope();
7773
+ init_code_style();
7630
7774
  init_goal_spec();
7631
7775
  PIPELINES = {
7632
7776
  feature: ["Brainstorm", "Objective", "Spec", "Plan", "Execute"],
@@ -7706,6 +7850,7 @@ ${lines2.join("\n")}` : "";
7706
7850
  };
7707
7851
  writesCode = (flow) => PIPELINES[flow].includes("Execute") || PIPELINES[flow].includes("Goal");
7708
7852
  scopeClause = (flow, scope) => scope && writesCode(flow) ? verifyScopeClause(scope) : "";
7853
+ codeStyleClause = (flow) => writesCode(flow) ? CODE_STYLE_CLAUSE : "";
7709
7854
  resumeClause = (r, branchTo, hasPlan = true) => {
7710
7855
  const fase = r.recorded.length ? `Fase yang SUDAH tercatat di $HANOMAN_PHASE_FILE: ${r.recorded.join(" \xB7 ")}. JANGAN mengulang fase itu dan JANGAN menulis ulang barisnya.` : "Belum ada fase yang tercatat di $HANOMAN_PHASE_FILE \u2014 worktree ini sendiri yang jadi alasan melanjutkan.";
7711
7856
  const lanjut = r.next ? `Lanjutkan dari fase: ${r.next}.` : hasPlan ? "Semua fase sudah tercatat. Periksa apakah plan di `docs/superpowers/plans/**` masih menyisakan task `- [ ]` dan selesaikan sisanya; bila sudah bersih, tinggal commit & push." : "Semua fase sudah tercatat. Buktikan sekali lagi goal-nya benar-benar tercapai, lalu commit & push.";
@@ -8034,10 +8179,14 @@ var init_agent_cli = __esm({
8034
8179
  function agentPromptOf(def2, roster) {
8035
8180
  const can = liveMentions(def2, roster);
8036
8181
  if (can.length === 0) {
8037
- return `${def2.instructions}
8038
-
8039
- ---
8040
- Kamu TIDAK boleh mendelegasikan ke agen lain. Selesaikan sendiri lalu laporkan hasilnya.`;
8182
+ return [
8183
+ def2.instructions,
8184
+ "",
8185
+ "---",
8186
+ "Kamu TIDAK boleh mendelegasikan ke agen lain. Selesaikan sendiri lalu laporkan hasilnya.",
8187
+ "",
8188
+ CODE_STYLE_CLAUSE
8189
+ ].join("\n");
8041
8190
  }
8042
8191
  const list2 = can.map((m) => `@${m}`).join(", ");
8043
8192
  return [
@@ -8046,7 +8195,9 @@ Kamu TIDAK boleh mendelegasikan ke agen lain. Selesaikan sendiri lalu laporkan h
8046
8195
  "---",
8047
8196
  `Kamu boleh mendelegasikan HANYA ke: ${list2}. Panggil lewat ${MENTION_TOOL} dengan nama agennya.`,
8048
8197
  `Anggaran rantai delegasi seluruh sesi ini ${MENTION_MAX_HOPS} hop. Bila kamu sudah berada di hop ke-${MENTION_MAX_HOPS}, JANGAN mendelegasikan lagi \u2014 selesaikan sendiri lalu laporkan.`,
8049
- "Sebutkan hop keberapa kamu berada saat mendelegasikan, dan jangan pernah memanggil agen yang sudah ada di rantai yang membawamu ke sini."
8198
+ "Sebutkan hop keberapa kamu berada saat mendelegasikan, dan jangan pernah memanggil agen yang sudah ada di rantai yang membawamu ke sini.",
8199
+ "",
8200
+ CODE_STYLE_CLAUSE
8050
8201
  ].join("\n");
8051
8202
  }
8052
8203
  function renderAgentsJson(defs) {
@@ -8091,6 +8242,7 @@ var init_custom_agents = __esm({
8091
8242
  "../runner/src/custom-agents.ts"() {
8092
8243
  "use strict";
8093
8244
  init_src();
8245
+ init_code_style();
8094
8246
  liveMentions = (def2, roster) => {
8095
8247
  const names = new Set(roster.map((r) => r.name));
8096
8248
  return def2.mentions.filter((m) => names.has(m) && m !== def2.name);
@@ -8231,6 +8383,7 @@ var init_src2 = __esm({
8231
8383
  init_agent_cli();
8232
8384
  init_custom_agents();
8233
8385
  init_verify_scope();
8386
+ init_code_style();
8234
8387
  init_paths();
8235
8388
  init_telegram_operator();
8236
8389
  }
@@ -9716,7 +9869,12 @@ var init_sync = __esm({
9716
9869
  // SPEC-516 · ADR-0105 · doneAt ikut menyeberang — cermin createdAt/startedAt. Tanpa ini spec
9717
9870
  // asal-hub mendarat di tiap client dengan doneAt null tanpa satu pun error, dan changelog
9718
9871
  // mode backlog di client itu selamanya kosong.
9719
- spec: ["projectId", "title", "source", "stage", "priority", "author", "objective", "payload", "branchFrom", "baseSha", "headSha", "dependsOn", "createdAt", "startedAt", "doneAt", "updatedAt"],
9872
+ // SPEC-546 · ADR-0109 · sourceHistory ikut menyeberang: jejak konversi type adalah bagian
9873
+ // keadaan yang harus dilihat sama oleh semua mesin. `upsert` yang tak menyebut sebuah kolom
9874
+ // TETAP berhasil, jadi kolom yang terlewat di sini mendarat sebagai null palsu di tiap client
9875
+ // tanpa satu pun error (kelas gagal-senyap ADR-0090/0093/0094/0105). BUKAN DATE_FIELDS —
9876
+ // `at` hidup di dalam JSON-nya, kolomnya sendiri bukan DateTime.
9877
+ spec: ["projectId", "title", "source", "stage", "priority", "author", "objective", "payload", "branchFrom", "baseSha", "headSha", "dependsOn", "sourceHistory", "createdAt", "startedAt", "doneAt", "updatedAt"],
9720
9878
  vps: ["name", "host", "port", "user", "health", "audit", "hardened", "lastSeenAt", "lastAuditAt", "updatedAt"],
9721
9879
  sessionResult: ["projectId", "specId", "oldStage", "newStage", "commitSha", "branch", "prUrl", "status", "deviceId", "author", "createdAt", "updatedAt"],
9722
9880
  // SPEC-268 · ADR-0066 · metadata tiket (lampiran biner tak disync). accessKeyHash wajib
@@ -18925,6 +19083,7 @@ async function projects_default(app2) {
18925
19083
 
18926
19084
  // src/routes/specs.ts
18927
19085
  init_src();
19086
+ init_src2();
18928
19087
  import { existsSync as existsSync4 } from "node:fs";
18929
19088
 
18930
19089
  // src/services/integrate.ts
@@ -19287,6 +19446,148 @@ async function nextSpecId(repoDir) {
19287
19446
  return `SPEC-${maxNum(ids, floor) + 1}`;
19288
19447
  }
19289
19448
 
19449
+ // src/services/spec-fields.ts
19450
+ init_src();
19451
+ function deriveSpecFields(source, payload, manualPriority) {
19452
+ if (source === "goal") {
19453
+ const pick2 = (v) => typeof v === "string" ? v.trim() : "";
19454
+ return {
19455
+ priority: manualPriority,
19456
+ objective: pick2(payload?.goal) || pick2(payload?.done) || "\u2014 goal belum diisi."
19457
+ };
19458
+ }
19459
+ const isQa = source === "qa";
19460
+ const priority = isQa && payload && "severity" in payload ? priorityFromSeverity(payload.severity) : manualPriority;
19461
+ const objective = isQa && payload && "actual" in payload ? payload.actual || payload.steps || "\u2014 audit untuk menelusuri akar masalah." : payload && "outcome" in payload ? payload.outcome || payload.context || "\u2014 brainstorm untuk memperjelas objective." : "";
19462
+ return { priority, objective };
19463
+ }
19464
+
19465
+ // src/services/spec-source.ts
19466
+ init_src();
19467
+ function checkSourceChange(spec, to, payload) {
19468
+ const started2 = spec.stage !== "brainstorming" || spec.baseSha !== null;
19469
+ if (started2) {
19470
+ if (flowForSource(spec.source) !== flowForSource(to))
19471
+ return {
19472
+ ok: false,
19473
+ code: 409,
19474
+ error: "backlog item sudah dimulai \u2014 type hanya bisa pindah ke source dengan flow yang sama"
19475
+ };
19476
+ if (payload !== void 0)
19477
+ return { ok: false, code: 409, error: "backlog item sudah dimulai \u2014 isinya tak bisa diubah" };
19478
+ return { ok: true, payload: spec.payload ?? {}, dropped: [] };
19479
+ }
19480
+ if (payload !== void 0) {
19481
+ if (!payloadMatchesSource(to, payload))
19482
+ return { ok: false, code: 400, error: "bentuk payload tak cocok dengan source" };
19483
+ return { ok: true, payload, dropped: [] };
19484
+ }
19485
+ const c = convertPayload(to, spec.payload);
19486
+ return { ok: true, payload: c.payload, dropped: c.dropped };
19487
+ }
19488
+ function sourceChangeEntry(spec, to, by, at) {
19489
+ return { at: at.toISOString(), from: spec.source, to, by, payload: spec.payload ?? null };
19490
+ }
19491
+ function appendSourceHistory(current, entry) {
19492
+ return [...Array.isArray(current) ? current : [], entry];
19493
+ }
19494
+
19495
+ // src/services/notifications.ts
19496
+ init_db();
19497
+ init_pty();
19498
+ async function recordDrift(vpsId, vpsName, drift, snapshotId) {
19499
+ if (drift.length === 0) return;
19500
+ const ids = drift.map((d) => d.itemId);
19501
+ const shown = ids.slice(0, 5).join(", ") + (ids.length > 5 ? `, +${ids.length - 5} lagi` : "");
19502
+ const title = `Drift di "${vpsName}": ${drift.length} item regresi (${shown})`;
19503
+ await prisma.notification.create({
19504
+ data: { type: "drift", key: `drift:${vpsId}:${snapshotId}`, title, projectId: null }
19505
+ }).catch(() => {
19506
+ });
19507
+ }
19508
+ async function recordCompletion(specId, title, projectId) {
19509
+ await prisma.spec.updateMany({ where: { id: specId, doneAt: null }, data: { doneAt: /* @__PURE__ */ new Date() } }).catch(() => {
19510
+ });
19511
+ const sessionId2 = specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_");
19512
+ await prisma.notification.create({
19513
+ data: { type: "done", key: `done:${specId}`, specId, sessionId: sessionId2, title, projectId }
19514
+ }).catch(() => {
19515
+ });
19516
+ }
19517
+ async function recordFailure(specId, title, projectId, reason) {
19518
+ const sessionId2 = specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_");
19519
+ await prisma.notification.create({
19520
+ data: { type: "fail", key: `fail:${specId}`, specId, sessionId: sessionId2, title: `Gagal: ${title} \u2014 ${reason}`, projectId }
19521
+ }).catch(() => {
19522
+ });
19523
+ }
19524
+ async function recordAutoMerge(specId, projectId, title) {
19525
+ const sessionId2 = specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_");
19526
+ await prisma.notification.create({
19527
+ data: { type: "automerge", key: `automerge:${specId}`, specId, sessionId: sessionId2, title, projectId }
19528
+ }).catch(() => {
19529
+ });
19530
+ }
19531
+ async function recordSourceChange(specId, projectId, title, from, to, seq) {
19532
+ const sessionId2 = specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_");
19533
+ await prisma.notification.create({
19534
+ data: {
19535
+ type: "spec-source",
19536
+ key: `source:${specId}:${seq}`,
19537
+ specId,
19538
+ sessionId: sessionId2,
19539
+ title: `${specId} \xB7 type ${from} \u2192 ${to} \u2014 ${title}`,
19540
+ projectId
19541
+ }
19542
+ }).catch(() => {
19543
+ });
19544
+ }
19545
+ async function recordNewTicket(ticketId, projectId, projectName, category, title) {
19546
+ const short = title.length > 80 ? title.slice(0, 77) + "\u2026" : title;
19547
+ const t = `Keluhan baru di "${projectName}": ${category}: ${short}`;
19548
+ await prisma.notification.create({
19549
+ data: { type: "ticket", key: `ticket:${ticketId}`, projectId, title: t }
19550
+ }).catch(() => {
19551
+ });
19552
+ }
19553
+ async function recordLeadDecision(decisionId, title, projectId, specId, sessionId2) {
19554
+ await prisma.notification.create({
19555
+ data: { type: "lead", key: `lead:${decisionId}`, specId, sessionId: sessionId2, projectId, title }
19556
+ }).catch(() => {
19557
+ });
19558
+ }
19559
+ var awaiting = /* @__PURE__ */ new Set();
19560
+ async function scanDecisions(read = liveDecisions) {
19561
+ const next = /* @__PURE__ */ new Set();
19562
+ const fresh = [];
19563
+ for (const s2 of read()) {
19564
+ if (!markerFilled(s2.decisionFile)) continue;
19565
+ next.add(s2.id);
19566
+ if (!awaiting.has(s2.id)) fresh.push(s2);
19567
+ }
19568
+ awaiting = next;
19569
+ for (const s2 of fresh) {
19570
+ const title = s2.specId ? (await prisma.spec.findUnique({ where: { id: s2.specId }, select: { title: true } }))?.title ?? s2.specId : s2.id;
19571
+ await prisma.notification.create({
19572
+ data: { type: "decision", specId: s2.specId ?? null, sessionId: s2.id, projectId: s2.projectId || null, title }
19573
+ });
19574
+ }
19575
+ }
19576
+ var DEFAULT_FEED_TAKE = 50;
19577
+ async function notificationsFeed(p = {}) {
19578
+ await scanDecisions();
19579
+ const pageSize = p.limit ? Math.max(1, Math.floor(+p.limit) || 1) : DEFAULT_FEED_TAKE;
19580
+ const page = p.page ? Math.max(1, Math.floor(+p.page) || 1) : 1;
19581
+ const total = await prisma.notification.count();
19582
+ const items = await prisma.notification.findMany({
19583
+ orderBy: { createdAt: "desc" },
19584
+ skip: (page - 1) * pageSize,
19585
+ take: pageSize
19586
+ });
19587
+ const unread = await prisma.notification.count({ where: { readAt: null } });
19588
+ return { items, unread, total, page, pageSize };
19589
+ }
19590
+
19290
19591
  // src/routes/specs.ts
19291
19592
  init_stage_machine();
19292
19593
 
@@ -21944,88 +22245,6 @@ init_pty();
21944
22245
  init_session_phases();
21945
22246
  init_stage_machine();
21946
22247
 
21947
- // src/services/notifications.ts
21948
- init_db();
21949
- init_pty();
21950
- async function recordDrift(vpsId, vpsName, drift, snapshotId) {
21951
- if (drift.length === 0) return;
21952
- const ids = drift.map((d) => d.itemId);
21953
- const shown = ids.slice(0, 5).join(", ") + (ids.length > 5 ? `, +${ids.length - 5} lagi` : "");
21954
- const title = `Drift di "${vpsName}": ${drift.length} item regresi (${shown})`;
21955
- await prisma.notification.create({
21956
- data: { type: "drift", key: `drift:${vpsId}:${snapshotId}`, title, projectId: null }
21957
- }).catch(() => {
21958
- });
21959
- }
21960
- async function recordCompletion(specId, title, projectId) {
21961
- await prisma.spec.updateMany({ where: { id: specId, doneAt: null }, data: { doneAt: /* @__PURE__ */ new Date() } }).catch(() => {
21962
- });
21963
- const sessionId2 = specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_");
21964
- await prisma.notification.create({
21965
- data: { type: "done", key: `done:${specId}`, specId, sessionId: sessionId2, title, projectId }
21966
- }).catch(() => {
21967
- });
21968
- }
21969
- async function recordFailure(specId, title, projectId, reason) {
21970
- const sessionId2 = specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_");
21971
- await prisma.notification.create({
21972
- data: { type: "fail", key: `fail:${specId}`, specId, sessionId: sessionId2, title: `Gagal: ${title} \u2014 ${reason}`, projectId }
21973
- }).catch(() => {
21974
- });
21975
- }
21976
- async function recordAutoMerge(specId, projectId, title) {
21977
- const sessionId2 = specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_");
21978
- await prisma.notification.create({
21979
- data: { type: "automerge", key: `automerge:${specId}`, specId, sessionId: sessionId2, title, projectId }
21980
- }).catch(() => {
21981
- });
21982
- }
21983
- async function recordNewTicket(ticketId, projectId, projectName, category, title) {
21984
- const short = title.length > 80 ? title.slice(0, 77) + "\u2026" : title;
21985
- const t = `Keluhan baru di "${projectName}": ${category}: ${short}`;
21986
- await prisma.notification.create({
21987
- data: { type: "ticket", key: `ticket:${ticketId}`, projectId, title: t }
21988
- }).catch(() => {
21989
- });
21990
- }
21991
- async function recordLeadDecision(decisionId, title, projectId, specId, sessionId2) {
21992
- await prisma.notification.create({
21993
- data: { type: "lead", key: `lead:${decisionId}`, specId, sessionId: sessionId2, projectId, title }
21994
- }).catch(() => {
21995
- });
21996
- }
21997
- var awaiting = /* @__PURE__ */ new Set();
21998
- async function scanDecisions(read = liveDecisions) {
21999
- const next = /* @__PURE__ */ new Set();
22000
- const fresh = [];
22001
- for (const s2 of read()) {
22002
- if (!markerFilled(s2.decisionFile)) continue;
22003
- next.add(s2.id);
22004
- if (!awaiting.has(s2.id)) fresh.push(s2);
22005
- }
22006
- awaiting = next;
22007
- for (const s2 of fresh) {
22008
- const title = s2.specId ? (await prisma.spec.findUnique({ where: { id: s2.specId }, select: { title: true } }))?.title ?? s2.specId : s2.id;
22009
- await prisma.notification.create({
22010
- data: { type: "decision", specId: s2.specId ?? null, sessionId: s2.id, projectId: s2.projectId || null, title }
22011
- });
22012
- }
22013
- }
22014
- var DEFAULT_FEED_TAKE = 50;
22015
- async function notificationsFeed(p = {}) {
22016
- await scanDecisions();
22017
- const pageSize = p.limit ? Math.max(1, Math.floor(+p.limit) || 1) : DEFAULT_FEED_TAKE;
22018
- const page = p.page ? Math.max(1, Math.floor(+p.page) || 1) : 1;
22019
- const total = await prisma.notification.count();
22020
- const items = await prisma.notification.findMany({
22021
- orderBy: { createdAt: "desc" },
22022
- skip: (page - 1) * pageSize,
22023
- take: pageSize
22024
- });
22025
- const unread = await prisma.notification.count({ where: { readAt: null } });
22026
- return { items, unread, total, page, pageSize };
22027
- }
22028
-
22029
22248
  // src/services/spec-head.ts
22030
22249
  init_db();
22031
22250
  init_src2();
@@ -22076,19 +22295,6 @@ async function liveSpecs(filter = {}) {
22076
22295
 
22077
22296
  // src/routes/specs.ts
22078
22297
  var branchUnknown = async (repoDir, branch) => !(await branchFromCandidates(repoDir)).includes(branch);
22079
- function deriveSpecFields(source, payload, manualPriority) {
22080
- if (source === "goal") {
22081
- const pick2 = (v) => typeof v === "string" ? v.trim() : "";
22082
- return {
22083
- priority: manualPriority,
22084
- objective: pick2(payload?.goal) || pick2(payload?.done) || "\u2014 goal belum diisi."
22085
- };
22086
- }
22087
- const isQa = source === "qa";
22088
- const priority = isQa && payload && "severity" in payload ? payload.severity === "minor" ? "sedang" : "tinggi" : manualPriority;
22089
- const objective = isQa && payload && "actual" in payload ? payload.actual || payload.steps || "\u2014 audit untuk menelusuri akar masalah." : payload && "outcome" in payload ? payload.outcome || payload.context || "\u2014 brainstorm untuk memperjelas objective." : "";
22090
- return { priority, objective };
22091
- }
22092
22298
  function filterSpecs(specs, f) {
22093
22299
  const needle = (f.q ?? "").trim().toLowerCase();
22094
22300
  const from = dayStart(f.from);
@@ -22241,6 +22447,40 @@ ${item.context}` : item.context;
22241
22447
  await notifySynced("spec", id);
22242
22448
  return updated;
22243
22449
  });
22450
+ app2.post("/specs/:id/source", async (req, reply) => {
22451
+ const { id } = req.params;
22452
+ const parsed = zChangeSpecSource.safeParse(req.body);
22453
+ if (!parsed.success) return reply.code(400).send({ error: parsed.error.flatten() });
22454
+ const spec = await prisma.spec.findUnique({ where: { id } });
22455
+ if (!spec) return reply.code(404).send({ error: "not found" });
22456
+ const to = parsed.data.source;
22457
+ if (to === spec.source) return reply.code(400).send({ error: "source tak berubah" });
22458
+ const gate = checkSourceChange(spec, to, parsed.data.payload);
22459
+ if (!gate.ok) return reply.code(gate.code).send({ error: gate.error });
22460
+ const by = req.user?.email ?? "system";
22461
+ const history = appendSourceHistory(
22462
+ spec.sourceHistory,
22463
+ sourceChangeEntry(spec, to, by, /* @__PURE__ */ new Date())
22464
+ );
22465
+ const { priority, objective } = deriveSpecFields(
22466
+ to,
22467
+ gate.payload,
22468
+ gate.payload.priority ?? spec.priority
22469
+ );
22470
+ const updated = await prisma.spec.update({
22471
+ where: { id },
22472
+ data: {
22473
+ source: to,
22474
+ payload: gate.payload,
22475
+ priority,
22476
+ objective,
22477
+ sourceHistory: history
22478
+ }
22479
+ });
22480
+ await recordSourceChange(spec.id, spec.projectId, spec.title, spec.source, to, history.length);
22481
+ await notifySynced("spec", id);
22482
+ return updated;
22483
+ });
22244
22484
  app2.get("/specs/:id/docs", async (req) => ({ files: await listSpecDocs(req.params.id) }));
22245
22485
  app2.get("/specs/:id/docs/*", async (req, reply) => {
22246
22486
  const { id } = req.params;
@@ -22295,6 +22535,9 @@ ${item.context}` : item.context;
22295
22535
  `hanoman \xB7 selesaikan konflik ${r.op} branch \`${sourceBranch(spec.id)}\` ${r.op === "merge" ? "ke" : "di atas"} \`${r.target}\`.`,
22296
22536
  `Kamu berada di worktree yang tertinggal di tengah operasi ${r.op} dengan konflik. Resolve konflik pada file bertanda, jaga kedua sisi perubahan sesuai maksudnya.`,
22297
22537
  r.finalize,
22538
+ // SPEC-543 · ADR-0108 · menyelesaikan konflik selalu berarti menyunting kode, dan prompt ini
22539
+ // dirakit inline di route — gerbang `writesCode` di runner/src/prompt.ts tak menjangkaunya.
22540
+ CODE_STYLE_CLAUSE,
22298
22541
  `Backlog item ${spec.id} \u2014 ${spec.title}.`
22299
22542
  ].join("\n\n");
22300
22543
  const s2 = createSession(spec.projectId, r.worktree, {
@@ -22543,6 +22786,7 @@ async function docs_default(app2) {
22543
22786
  }
22544
22787
 
22545
22788
  // src/routes/ide.ts
22789
+ init_src2();
22546
22790
  import { basename } from "node:path";
22547
22791
  import { spawn as spawn2 } from "node:child_process";
22548
22792
 
@@ -23501,6 +23745,8 @@ async function finishGraphOp(reply, id, repoDir, r, verb) {
23501
23745
  `hanoman \xB7 selesaikan konflik ${verb} \`${r.source}\` \u2192 \`${r.target}\`.`,
23502
23746
  `Kamu berada di worktree yang tertinggal di tengah ${verb} dengan konflik. Resolve konflik pada file bertanda, jaga kedua sisi perubahan sesuai maksudnya.`,
23503
23747
  r.finalize,
23748
+ // SPEC-543 · ADR-0108 · cermin pintu konflik backlog di routes/specs.ts.
23749
+ CODE_STYLE_CLAUSE,
23504
23750
  `${verb} via git graph project ${id}.`
23505
23751
  ].join("\n\n");
23506
23752
  const s2 = createSession(id, r.worktree, { id: basename(r.worktree), model, effort, agent, prompt });
@@ -23963,6 +24209,8 @@ async function terminal_default(app2) {
23963
24209
  `hanoman \xB7 selesaikan konflik ${r.op} branch \`${s2.branch}\` ${r.op === "merge" ? "ke" : "di atas"} \`${r.target}\`.`,
23964
24210
  `Kamu berada di worktree yang tertinggal di tengah operasi ${r.op} dengan konflik. Resolve konflik pada file bertanda, jaga kedua sisi perubahan sesuai maksudnya.`,
23965
24211
  r.finalize,
24212
+ // SPEC-543 · ADR-0108 · cermin pintu konflik backlog di routes/specs.ts.
24213
+ CODE_STYLE_CLAUSE,
23966
24214
  `Sesi PRD ${s2.id}.`
23967
24215
  ].join("\n\n");
23968
24216
  const cs = createSession(s2.projectId, r.worktree, {
@@ -26342,6 +26590,7 @@ init_pty();
26342
26590
 
26343
26591
  // src/services/lead/prompt.ts
26344
26592
  init_src();
26593
+ init_src2();
26345
26594
  var bullet2 = (s2) => `- ${s2}`;
26346
26595
  function leadPrompt(q, c) {
26347
26596
  const lines2 = [];
@@ -26405,6 +26654,8 @@ function leadPrompt(q, c) {
26405
26654
  lines2.push(` ${LEAD_ACTIONS.join(" \xB7 ")}`);
26406
26655
  lines2.push(" Deploy, perintah/konsol VPS, data produksi, dan penghapusan apa pun (project, backlog, branch, worktree, notifikasi, jejak) TERKUNCI dan tidak akan pernah dijalankan.");
26407
26656
  lines2.push("");
26657
+ lines2.push(CODE_STYLE_CLAUSE);
26658
+ lines2.push("");
26408
26659
  lines2.push("## Sepanjang apa (WAJIB)");
26409
26660
  lines2.push(`Peminta jawabanmu adalah MESIN yang sedang menunggu, bukan pembaca laporan. \`decision\` paling banyak ${LEAD_DECISION_MAX} karakter (satu kalimat) dan \`reason\` paling banyak ${LEAD_REASON_MAX} karakter (dua-tiga kalimat). Yang lebih panjang dipangkas server sebelum sampai ke peminta.`);
26410
26661
  lines2.push("JANGAN menuliskan: ringkasan ulang konteks yang sudah kamu terima di atas, latar belakang atau sejarah masalahnya, daftar alternatif yang tak diminta, maupun rencana kerja bertahap. Langsung putusannya dan alasannya.");
@@ -28206,6 +28457,7 @@ async function changelogAgentDefaults() {
28206
28457
  }
28207
28458
 
28208
28459
  // src/services/changelog/render.ts
28460
+ init_src2();
28209
28461
  var MODE_LABEL = {
28210
28462
  backlog: "backlog yang selesai dalam rentang tanggal",
28211
28463
  commit: "riwayat perubahan repo dalam rentang yang dipilih",
@@ -28255,6 +28507,10 @@ function changelogPrompt(input, budgetMs) {
28255
28507
  "- Gabungkan bahan yang bicara hal yang sama jadi satu butir. 3\u201310 butir; kurangi bila memang sedikit.",
28256
28508
  "- Jangan mengarang perubahan yang tak ada di bahan.",
28257
28509
  "",
28510
+ // SPEC-543 · ADR-0108 · dipasang di SEMUA prompt agen, bukan hanya yang menulis kode; gerbang
28511
+ // di baris pertama klausa yang membuatnya diam untuk narator ini.
28512
+ CODE_STYLE_CLAUSE,
28513
+ "",
28258
28514
  "Bentuk keluaran \u2014 HANYA markdown ini, tanpa kalimat pembuka atau penutup di luarnya,",
28259
28515
  "tanpa blok kode:",
28260
28516
  "",