harness-everything 0.32.0 → 0.34.0

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 (103) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -1
  3. package/CHANGELOG.md +33 -0
  4. package/README.md +3 -2
  5. package/ci/fixtures/doc-lifecycle/prd-revisions/config.json +9 -0
  6. package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/config.json +20 -0
  7. package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/expected.json +12 -0
  8. package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/invalid-fork/docs/prd/graph/r001.md +20 -0
  9. package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/invalid-fork/docs/prd/graph/r002.md +26 -0
  10. package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/invalid-fork/docs/prd/graph/r003.md +26 -0
  11. package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/valid/docs/prd/graph/r001.md +20 -0
  12. package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/valid/docs/prd/graph/r002.md +26 -0
  13. package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r001.body.md +39 -0
  14. package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r002-dropped.body.md +35 -0
  15. package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r002-incomplete.body.md +37 -0
  16. package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r002-one-replaced.body.md +39 -0
  17. package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r002-overlap.body.md +39 -0
  18. package/ci/fixtures/doc-lifecycle/scenarios/active-to-active/config.json +1 -0
  19. package/ci/fixtures/doc-lifecycle/scenarios/active-to-active/docs/adr/GRPH-001.md +8 -0
  20. package/ci/fixtures/doc-lifecycle/scenarios/active-to-active/docs/adr/GRPH-002.md +9 -0
  21. package/ci/fixtures/doc-lifecycle/scenarios/active-to-active/expected.json +1 -0
  22. package/ci/fixtures/doc-lifecycle/scenarios/active-to-active-mismatch/config.json +1 -0
  23. package/ci/fixtures/doc-lifecycle/scenarios/active-to-active-mismatch/docs/adr/GRPH-001.md +8 -0
  24. package/ci/fixtures/doc-lifecycle/scenarios/active-to-active-mismatch/docs/adr/GRPH-002.md +9 -0
  25. package/ci/fixtures/doc-lifecycle/scenarios/active-to-active-mismatch/expected.json +1 -0
  26. package/ci/fixtures/doc-lifecycle/scenarios/active-to-legacy/config.json +1 -0
  27. package/ci/fixtures/doc-lifecycle/scenarios/active-to-legacy/docs/adr/GRPH-010.md +9 -0
  28. package/ci/fixtures/doc-lifecycle/scenarios/active-to-legacy/docs/adr/legacy/GRPH-007.md +8 -0
  29. package/ci/fixtures/doc-lifecycle/scenarios/active-to-legacy/expected.json +1 -0
  30. package/ci/fixtures/doc-lifecycle/scenarios/alias-rename/config.json +1 -0
  31. package/ci/fixtures/doc-lifecycle/scenarios/alias-rename/docs/adr/GRAPH-007.md +9 -0
  32. package/ci/fixtures/doc-lifecycle/scenarios/alias-rename/docs/adr/legacy/GRPH-007.md +8 -0
  33. package/ci/fixtures/doc-lifecycle/scenarios/alias-rename/expected.json +1 -0
  34. package/ci/fixtures/doc-lifecycle/scenarios/duplicate-legacy/config.json +1 -0
  35. package/ci/fixtures/doc-lifecycle/scenarios/duplicate-legacy/docs/adr/legacy/v1/GRPH-007.md +8 -0
  36. package/ci/fixtures/doc-lifecycle/scenarios/duplicate-legacy/docs/adr/legacy/v2/GRPH-007.md +8 -0
  37. package/ci/fixtures/doc-lifecycle/scenarios/duplicate-legacy/expected.json +1 -0
  38. package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/config.json +1 -0
  39. package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-003.md +9 -0
  40. package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-004.md +9 -0
  41. package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-005.md +9 -0
  42. package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-006.md +9 -0
  43. package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-007.md +9 -0
  44. package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/expected.json +1 -0
  45. package/ci/fixtures/doc-lifecycle/scenarios/pending-edge/config.json +1 -0
  46. package/ci/fixtures/doc-lifecycle/scenarios/pending-edge/docs/adr/GRPH-020.md +8 -0
  47. package/ci/fixtures/doc-lifecycle/scenarios/pending-edge/docs/adr/GRPH-021.md +9 -0
  48. package/ci/fixtures/doc-lifecycle/scenarios/pending-edge/expected.json +1 -0
  49. package/ci/fixtures/doc-lifecycle/scenarios/recorded-conflict/config.json +1 -0
  50. package/ci/fixtures/doc-lifecycle/scenarios/recorded-conflict/docs/adr/GRPH-003.md +8 -0
  51. package/ci/fixtures/doc-lifecycle/scenarios/recorded-conflict/docs/prd/graph.md +10 -0
  52. package/ci/fixtures/doc-lifecycle/scenarios/recorded-conflict/expected.json +1 -0
  53. package/ci/mechanism-2aw-project-docs-config.test.js +759 -0
  54. package/ci/mechanism-2ax-doc-lifecycle.test.js +1061 -0
  55. package/ci/mechanism-2ay-prd-revisions.test.js +2891 -0
  56. package/ci/mechanism-2az-step-type-registry.test.js +86 -0
  57. package/ci/openai-plugin-package.test.js +1 -1
  58. package/ci/release-consistency-check.js +26 -9
  59. package/ci/release-consistency-check.test.js +30 -0
  60. package/ci/router-workflow-plan.test.js +36 -0
  61. package/code-review/SKILL.md +41 -0
  62. package/code-review/references/review-playbook.md +40 -0
  63. package/code-review/templates/finding.template.md +33 -0
  64. package/docs/document-lifecycle.md +206 -0
  65. package/docs/openai-plugin.md +1 -1
  66. package/docs/workflows/code-review.md +58 -0
  67. package/evals/code-review/eval.yaml +26 -0
  68. package/evals/code-review/tasks/negative-grill-me.yaml +16 -0
  69. package/evals/code-review/tasks/negative-security-review.yaml +16 -0
  70. package/evals/code-review/tasks/negative-tdd.yaml +16 -0
  71. package/evals/code-review/tasks/negative-verification-loop.yaml +16 -0
  72. package/evals/code-review/tasks/positive-pr-review.yaml +16 -0
  73. package/evals/code-review/tasks/positive-revised-head-rereview.yaml +16 -0
  74. package/harness-everything/SKILL.md +1 -1
  75. package/harness-everything/references/skill-registry.md +1 -0
  76. package/harness-everything/scripts/doc-lifecycle.js +2401 -0
  77. package/harness-everything/scripts/project-docs-resolver.js +764 -0
  78. package/harness-everything/scripts/routing-keywords.json +7 -0
  79. package/hooks/scripts/lib/binding-resolver.js +1 -0
  80. package/multi-agent-workspace/SKILL.md +1 -1
  81. package/multi-agent-workspace/scripts/project-docs-resolver.js +437 -30
  82. package/opencode-plugin/plugin.json +1 -1
  83. package/package.json +2 -2
  84. package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
  85. package/plugins/harness-everything/hooks/scripts/lib/binding-resolver.js +1 -0
  86. package/plugins/harness-everything/plugin.json +1 -1
  87. package/plugins/harness-everything/skills/code-review/SKILL.md +41 -0
  88. package/plugins/harness-everything/skills/code-review/references/review-playbook.md +40 -0
  89. package/plugins/harness-everything/skills/code-review/templates/finding.template.md +33 -0
  90. package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
  91. package/plugins/harness-everything/skills/harness-everything/references/skill-registry.md +1 -0
  92. package/plugins/harness-everything/skills/harness-everything/scripts/doc-lifecycle.js +2401 -0
  93. package/plugins/harness-everything/skills/harness-everything/scripts/project-docs-resolver.js +764 -0
  94. package/plugins/harness-everything/skills/harness-everything/scripts/routing-keywords.json +7 -0
  95. package/plugins/harness-everything/skills/multi-agent-workspace/SKILL.md +1 -1
  96. package/plugins/harness-everything/skills/multi-agent-workspace/scripts/project-docs-resolver.js +437 -30
  97. package/plugins/harness-everything/skills/to-spec/SKILL.md +1 -1
  98. package/plugins/harness-everything/skills/to-spec/scripts/check-project-docs.js +168 -54
  99. package/plugins/harness-everything/skills/to-spec/scripts/project-docs-resolver.js +749 -96
  100. package/sbom.cdx.json +7 -7
  101. package/to-spec/SKILL.md +1 -1
  102. package/to-spec/scripts/check-project-docs.js +168 -54
  103. package/to-spec/scripts/project-docs-resolver.js +749 -96
@@ -4,23 +4,21 @@
4
4
  *
5
5
  * Mechanized gate for `to-spec` (and `to-tickets`, once installed): has this
6
6
  * repo already pinned down where reference docs live, how issues are
7
- * tracked, and what counts as a valid issue? Stored as a `projectDocs` entry
8
- * inside the SAME harness-everything/manifest.json this package already
9
- * owns (see scripts/lib/manifest.js and self-evolve's `generated` skill
10
- * registry) - not a new docs/agents/ file convention, so there's one config
11
- * surface, not two.
7
+ * tracked, and what counts as a valid issue? Read the validated project
8
+ * configuration first; its project-docs.json file wholly overrides host
9
+ * manifest values. Without it, host configuration is used only when selected
10
+ * unambiguously by the shared resolver.
12
11
  *
13
12
  * Deliberately repo-local only: unlike self-evolve's `generated` skills
14
13
  * (intentionally reusable across projects), doc location / tracker / issue
15
14
  * definition are PER-REPO facts. Reading or writing them through the global
16
15
  * ~/.agents or ~/.claude manifest homes would leak one project's tracker
17
- * into every other project sharing that global install, so this script
18
- * scans only the workspace-relative platform homes, never the user-home ones.
16
+ * into every other project sharing that global install, so the resolver reads
17
+ * only the workspace project file and workspace-relative platform manifests.
19
18
  *
20
- * Exit 0 -> at least one detected platform's manifest.json (inside this
21
- * repo) has a complete projectDocs entry. Read it, skip Step 0.
22
- * Exit 1 -> missing or incomplete everywhere. Run the Step 0 interview in
23
- * to-spec/SKILL.md for the flagged field(s), then persist with `init`.
19
+ * Exit 0 -> a complete effective config or allowed workspace inference exists.
20
+ * Exit 1 -> required fields are missing, or configuration is invalid/ambiguous.
21
+ * Run the Step 0 interview in to-spec/SKILL.md, then persist with `init`.
24
22
  *
25
23
  * manifest.json is read on every single tier-router.js invocation (every
26
24
  * prompt, not just to-spec runs) to scan for self-evolve's generated
@@ -39,7 +37,7 @@ const fs = require('fs');
39
37
  const path = require('path');
40
38
  // Keep project-doc path decisions in the resolver carried by the installed
41
39
  // multi-agent-workspace skill. This script consumes the same contract.
42
- const { getWorkspaceRoot, getRepoManifestHomes, detectProjectDocsConventions } =
40
+ const { getWorkspaceRoot, getRepoManifestHomes, detectProjectDocsConventions, validateDocsConfig } =
43
41
  require('./project-docs-resolver');
44
42
 
45
43
  function loadManifestHelper() {
@@ -102,33 +100,37 @@ function detectWorkspaceConventions(workspaceRoot) {
102
100
  }
103
101
 
104
102
  function runCheck(workspaceRoot) {
105
- const existingPaths = getRepoManifestHomes(workspaceRoot)
106
- .map(getManifestPath)
107
- .filter(p => fs.existsSync(p));
108
-
109
- // Check explicit projectDocs entries in manifest files
110
- for (const manifestPath of existingPaths) {
111
- const docs = readManifest(manifestPath).projectDocs;
112
- if (docs && missingFields(docs).length === 0) {
113
- console.log(`[Project Docs Check] OK: ${manifestPath} defines document location, issue tracker, and issue definition.`);
114
- process.exit(0);
103
+ // Selection and validation happen in the shared resolver. Invalid or
104
+ // ambiguous sources cannot authorize a destination or fall through to a
105
+ // different host; only `none` permits workspace convention inference.
106
+ const effective = detectWorkspaceConventions(workspaceRoot);
107
+ if (effective.status === 'invalid' || effective.status === 'ambiguous') {
108
+ console.log(`[Project Docs Check] ${effective.status.toUpperCase()}: no usable projectDocs selection.`);
109
+ for (const diagnostic of effective.diagnostics || []) {
110
+ console.log(` - ${diagnostic.code}: ${diagnostic.message} (${diagnostic.path})`);
115
111
  }
112
+ process.exit(1);
116
113
  }
117
114
 
118
- // Fallback to heuristic workspace detection
119
- const inferred = detectWorkspaceConventions(workspaceRoot);
120
- const missingInferred = missingFields(inferred);
121
-
122
- if (missingInferred.length === 0) {
123
- console.log(`[Project Docs Check] OK (Inferred from workspace structure):`);
124
- console.log(` - docLocation: ${inferred.docLocation}`);
125
- console.log(` - tracker: ${inferred.tracker}`);
126
- console.log(` - issueDefinition: ${inferred.issueDefinition}`);
115
+ // The resolver reports which required fields its selection left uncovered
116
+ // (configured-only for project/host selections, plus inference for none).
117
+ const missing = Array.isArray(effective.missing) ? effective.missing : missingFields(effective);
118
+ if (missing.length === 0) {
119
+ const source = effective.status === 'none'
120
+ ? 'Inferred from workspace structure'
121
+ : `Selected ${effective.status} projectDocs configuration`;
122
+ console.log(`[Project Docs Check] OK (${source}):`);
123
+ console.log(` - docLocation: ${effective.docLocation}`);
124
+ console.log(` - tracker: ${effective.tracker}`);
125
+ console.log(` - issueDefinition: ${effective.issueDefinition}`);
127
126
  process.exit(0);
128
127
  }
129
128
 
130
- console.log(`[Project Docs Check] INCOMPLETE: no manifest.json or standard folder structure covers all fields. Missing: ${missingInferred.join(', ')}`);
131
- console.log('=> Run the Step 0 framework interview for missing fields, or proceed using default workspace fallbacks.');
129
+ const source = effective.status === 'none'
130
+ ? 'Inferred from workspace structure'
131
+ : `Selected ${effective.status} projectDocs configuration`;
132
+ console.log(`[Project Docs Check] INCOMPLETE (${source}): missing fields: ${missing.join(', ')}`);
133
+ console.log('=> Run the Step 0 framework interview for missing fields, then persist with init; selected configs are not completed from workspace inference.');
132
134
  process.exit(1);
133
135
  }
134
136
 
@@ -144,13 +146,51 @@ function parseFlags(args) {
144
146
  return values;
145
147
  }
146
148
 
149
+ function isPlainObject(value) {
150
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
151
+ }
152
+
153
+ // init never overwrites a configured value: absent/empty required fields are
154
+ // filled, matching flags are accepted as no-ops, and a differing flag is a
155
+ // field-specific conflict that aborts before any write.
156
+ function mergeProjectFields(existing, flags, fields) {
157
+ const filled = [];
158
+ const conflicts = [];
159
+ const next = { ...existing };
160
+ for (const [flag, field] of Object.entries(fields)) {
161
+ const value = flags[flag];
162
+ const current = existing[field];
163
+ if (typeof current === 'string' && current.trim()) {
164
+ if (current !== value) {
165
+ conflicts.push({ flag, field, existing: current });
166
+ }
167
+ continue;
168
+ }
169
+ next[field] = value;
170
+ filled.push(field);
171
+ }
172
+ return { next, filled, conflicts };
173
+ }
174
+
175
+ function printConflicts(conflicts) {
176
+ for (const conflict of conflicts) {
177
+ console.error(`[Project Docs Check] --${conflict.flag} conflicts with the configured ${conflict.field} (existing "${conflict.existing}").`);
178
+ console.error('=> init fills only absent/empty fields and never overwrites configured values; remove the flag or update the configuration explicitly.');
179
+ }
180
+ }
181
+
147
182
  function runInit(workspaceRoot, args) {
148
183
  const flags = parseFlags(args);
149
- const missingFlags = Object.keys(FLAG_TO_FIELD).filter(f => !flags[f]);
184
+ // Required values must be non-empty after trimming, the same presence rule
185
+ // the resolver applies when reading the configuration; whitespace-only
186
+ // pointers are rejected before anything is read or written, and accepted
187
+ // values are stored trimmed.
188
+ const missingFlags = Object.keys(FLAG_TO_FIELD).filter(f => !flags[f] || !String(flags[f]).trim());
150
189
  if (missingFlags.length > 0) {
151
- console.error(`[Project Docs Check] init requires --${missingFlags.join(', --')}`);
190
+ console.error(`[Project Docs Check] init requires --${missingFlags.join(', --')} (values must not be empty or whitespace-only)`);
152
191
  process.exit(1);
153
192
  }
193
+ for (const f of Object.keys(FLAG_TO_FIELD)) flags[f] = String(flags[f]).trim();
154
194
 
155
195
  const oversizedFlags = Object.keys(FLAG_TO_FIELD).filter(f => flags[f].length > MAX_FIELD_LENGTH);
156
196
  if (oversizedFlags.length > 0) {
@@ -161,38 +201,112 @@ function runInit(workspaceRoot, args) {
161
201
  process.exit(1);
162
202
  }
163
203
 
164
- const projectDocs = {
165
- docLocation: flags['doc-location'],
166
- tracker: flags['tracker'],
167
- issueDefinition: flags['issue-definition'],
168
- updatedAt: new Date().toISOString(),
169
- };
204
+ // The project file wholly overrides host manifests, so an existing one is
205
+ // the authoritative repair target: fill only its absent/empty fields,
206
+ // preserve every other key, validate the candidate, and write only it.
207
+ const projectFile = path.join(workspaceRoot, '.agents', 'harness-everything', 'project-docs.json');
208
+ if (fs.existsSync(projectFile)) {
209
+ const rel = path.relative(workspaceRoot, projectFile).replace(/\\/g, '/');
210
+ let existing;
211
+ try {
212
+ existing = JSON.parse(fs.readFileSync(projectFile, 'utf8'));
213
+ } catch (error) {
214
+ console.error(`[Project Docs Check] ${rel} exists but is not valid JSON (${error.message}); leaving it untouched.`);
215
+ console.error('=> Fix the JSON syntax (or remove the file after saving its content elsewhere), then rerun init.');
216
+ process.exit(1);
217
+ }
218
+ if (!isPlainObject(existing)) {
219
+ console.error(`[Project Docs Check] ${rel} must contain a JSON object; leaving it untouched.`);
220
+ console.error('=> Fix the structure, then rerun init.');
221
+ process.exit(1);
222
+ }
223
+ const validation = validateDocsConfig(existing);
224
+ if (!validation.valid) {
225
+ console.error(`[Project Docs Check] ${rel} failed schema validation; leaving it untouched.`);
226
+ for (const diagnostic of validation.diagnostics) {
227
+ console.error(` - ${diagnostic.code}: ${diagnostic.message}`);
228
+ }
229
+ console.error('=> Fix the reported issues, then rerun init.');
230
+ process.exit(1);
231
+ }
232
+ const { next, filled, conflicts } = mergeProjectFields(existing, flags, FLAG_TO_FIELD);
233
+ if (conflicts.length > 0) {
234
+ printConflicts(conflicts);
235
+ process.exit(1);
236
+ }
237
+ if (filled.length === 0) {
238
+ console.log(`[Project Docs Check] ${rel} already covers all requested fields; nothing to fill, no write.`);
239
+ process.exit(0);
240
+ }
241
+ fs.writeFileSync(projectFile, `${JSON.stringify(next, null, 2)}\n`, 'utf8');
242
+ console.log(`[Project Docs Check] Updated ${rel}, filling: ${filled.join(', ')}; existing projectDocs keys preserved; host manifests untouched (the project file overrides them).`);
243
+ process.exit(0);
244
+ }
170
245
 
171
246
  const homes = getRepoManifestHomes(workspaceRoot);
172
247
 
173
248
  // Write to every platform home already bootstrapped with harness in THIS
174
249
  // repo (their harness-everything/ subfolder already exists), so switching
175
- // editors mid-project never loses this config.
176
- let wrote = 0;
250
+ // editors mid-project never loses this config. Existing projectDocs keys
251
+ // are preserved: only absent/empty required fields are filled here too.
252
+ const writableHomes = [];
177
253
  for (const home of homes) {
178
254
  const manifestPath = getManifestPath(home);
179
255
  if (!fs.existsSync(path.dirname(manifestPath))) continue;
180
256
  const data = readManifest(manifestPath);
181
- data.projectDocs = projectDocs;
182
- writeManifest(manifestPath, data);
183
- console.log(`[Project Docs Check] Wrote projectDocs to ${manifestPath}`);
184
- wrote++;
257
+ const existingProjectDocs = isPlainObject(data.projectDocs) ? data.projectDocs : null;
258
+ writableHomes.push({ manifestPath, data, existingProjectDocs });
185
259
  }
186
260
 
187
- // No repo-local platform bootstrapped yet: create the primary one
188
- // (.claude/harness-everything/manifest.json) rather than falling back to
189
- // any global/user-home manifest - this data must never leave the repo.
190
- if (wrote === 0) {
261
+ const plans = [];
262
+ const conflicts = [];
263
+ for (const home of writableHomes) {
264
+ // A home without projectDocs keeps the previous fresh-object behavior;
265
+ // an existing projectDocs object only receives absent/empty required
266
+ // fields, preserving governance and unknown keys.
267
+ const baseProjectDocs = home.existingProjectDocs || {
268
+ docLocation: flags['doc-location'],
269
+ tracker: flags['tracker'],
270
+ issueDefinition: flags['issue-definition'],
271
+ updatedAt: new Date().toISOString(),
272
+ };
273
+ const { next, filled, conflicts: homeConflicts } = mergeProjectFields(baseProjectDocs, flags, FLAG_TO_FIELD);
274
+ if (homeConflicts.length) {
275
+ conflicts.push({ manifestPath: home.manifestPath, homeConflicts });
276
+ continue;
277
+ }
278
+ plans.push({ write: filled.length > 0 || !home.existingProjectDocs, manifestPath: home.manifestPath, data: home.data, projectDocs: next, filled });
279
+ }
280
+
281
+ // Abort before the first write when ANY bootstrapped home conflicts, so a
282
+ // partial multi-editor update can never leave divergent manifests behind.
283
+ if (conflicts.length) {
284
+ for (const entry of conflicts) {
285
+ console.error(`[Project Docs Check] ${path.relative(workspaceRoot, entry.manifestPath).replace(/\\/g, '/')}:`);
286
+ printConflicts(entry.homeConflicts);
287
+ }
288
+ process.exit(1);
289
+ }
290
+
291
+ if (writableHomes.length === 0) {
191
292
  const manifestPath = getManifestPath(homes[0]);
192
293
  const data = readManifest(manifestPath);
193
- data.projectDocs = projectDocs;
294
+ data.projectDocs = {
295
+ docLocation: flags['doc-location'],
296
+ tracker: flags['tracker'],
297
+ issueDefinition: flags['issue-definition'],
298
+ updatedAt: new Date().toISOString(),
299
+ };
194
300
  writeManifest(manifestPath, data);
195
301
  console.log(`[Project Docs Check] No platform bootstrapped yet in this repo - wrote projectDocs to ${manifestPath}`);
302
+ process.exit(0);
303
+ }
304
+
305
+ for (const plan of plans) {
306
+ if (!plan.write) continue;
307
+ plan.data.projectDocs = plan.projectDocs;
308
+ writeManifest(plan.manifestPath, plan.data);
309
+ console.log(`[Project Docs Check] Wrote projectDocs to ${plan.manifestPath} (filled: ${plan.filled.join(', ')}; existing projectDocs keys preserved)`);
196
310
  }
197
311
 
198
312
  process.exit(0);
@@ -202,9 +316,9 @@ if (process.argv[2] === '--help' || process.argv[2] === '-h') {
202
316
  console.log(`Gate: has this repo already pinned doc location / issue tracker / issue definition?
203
317
 
204
318
  Usage:
205
- node check-project-docs.js [check] Exit 0 if a complete projectDocs entry already exists in this repo's manifest.json; exit 1 with the missing field(s) otherwise.
319
+ node check-project-docs.js [check] Exit 0 for complete effective config or allowed inference; exit 1 for missing, invalid, or ambiguous config.
206
320
  node check-project-docs.js init --doc-location "..." --tracker "..." --issue-definition "..."
207
- Persist all three fields (each <= ${MAX_FIELD_LENGTH} chars - a pointer, not the content) to every already-bootstrapped platform manifest.json in this repo.
321
+ Fill only absent/empty fields (each <= ${MAX_FIELD_LENGTH} chars - a pointer, not the content). With an existing .agents/harness-everything/project-docs.json, that file is validated and written; otherwise the fields go to every already-bootstrapped platform manifest.json in this repo, preserving existing projectDocs keys. Conflicting flags fail before any write.
208
322
 
209
323
  Repo-local only: writes only to workspace-relative manifest homes (.claude/, .cursor/,
210
324
  .github/, .codex/, .continue/), never to the user-home manifests self-evolve uses.`);