harness-everything 0.36.2 → 0.36.3
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +7 -0
- package/ci/mechanism-2aw-project-docs-config.test.js +450 -10
- package/docs/document-lifecycle.md +60 -8
- package/harness-everything/SKILL.md +1 -1
- package/harness-everything/scripts/project-docs-resolver.js +30 -13
- package/multi-agent-workspace/SKILL.md +1 -1
- package/multi-agent-workspace/scripts/project-docs-resolver.js +30 -13
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/project-docs-resolver.js +30 -13
- package/plugins/harness-everything/skills/multi-agent-workspace/SKILL.md +1 -1
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/project-docs-resolver.js +30 -13
- package/plugins/harness-everything/skills/to-spec/SKILL.md +1 -1
- package/plugins/harness-everything/skills/to-spec/scripts/check-project-docs.js +136 -4
- package/plugins/harness-everything/skills/to-spec/scripts/project-docs-resolver.js +30 -13
- package/sbom.cdx.json +7 -7
- package/to-spec/SKILL.md +1 -1
- package/to-spec/scripts/check-project-docs.js +136 -4
- package/to-spec/scripts/project-docs-resolver.js +30 -13
|
@@ -4,16 +4,68 @@ Contract for the documentation family: project docs configuration (D5), document
|
|
|
4
4
|
|
|
5
5
|
Documentation is a **consumer** of the #320 workflow/obligation abstractions. Nothing here is a second router, a standing maintenance loop or a cognitive lock: every check is a pure function that reports diagnostics.
|
|
6
6
|
|
|
7
|
-
## Scope of this slice
|
|
7
|
+
## Scope of this slice (status as of 2026-10-20)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
This table distinguishes merged mechanism code from end-to-end workflow integration. The last reviewed `main` commit is `29b1d65b398e2622af97bf067078373b7fa76b26` (v0.36.2). That release commit carries `[skip ci]` and only bumps package/distribution versions on top of `e5d22584e243d5b5e587ee0279625e97a9033160`, whose full-gate `CI` run [39374474269](https://github.com/dyphn1/Harness-everything/actions/runs/39374474269) completed with every job succeeded. The historical Windows System One resident teardown (`S1-RS15`) was observed in attempt 1 of [run 39175240370](https://github.com/dyphn1/Harness-everything/actions/runs/39175240370) on `main@75e04359ae5bf11a41739f8eb04d25a76ee8f3a7`; that run completed successfully on attempt 2 and says nothing about `29b1d65`. Treat the resident teardown as a known platform limitation only when a current run on the exact SHA reproduces it, and require an all-green full-gate run for that SHA before calling any baseline green.
|
|
10
|
+
|
|
11
|
+
| Phase / prerequisite | Verified implementation and remaining work |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| 0 — contract + RED fixtures | Agreed decisions in this document, #325 and #327; fixtures under `ci/fixtures/doc-lifecycle/`; preserve earlier design history |
|
|
14
|
+
| 1a — `project-docs.json` loader | **Merged** in #327; host-only malformed manifest protection remains a separate correction in #331 / draft PR #333 |
|
|
15
|
+
| 2 — roles, IDs, aliases, legacy, supersession, index, D6.9 PRD revisions | **Merged** in #327; do not rebuild this lifecycle logic for Phase 3 |
|
|
16
|
+
| Step-type prerequisite (#326) | **Complete:** PR #328 merged; Harness-owned `doc-change` is explicitly declared, has no implicit TDD binding, and is not auto-inserted by router/System One |
|
|
17
|
+
| 3a — document facts, obligations and evidence producer | **Merged** in #334: `harness-everything/scripts/doc-obligations.js` and `doc-evidence.js`. H3/H4 have *partial deterministic mechanism* coverage, not integrated workflow evidence. Known limitations: caller-attested envelope; `configDigest` presence-only; `candidateDigest` restricted to `docs/prd/`; missing independent task-scope binding. Deleted and old rename paths are bound to trusted per-path baseline evidence ([doc-evidence-baseline-binding.md](doc-evidence-baseline-binding.md), #335 fixed by #342), with workflow-disposition reconciliation left out of scope |
|
|
18
|
+
| 3b — pilot-local provider suggestions and workflow evidence reconciliation | **Next implementation slice, not started; #341-gated.** Provider metadata, adapter/interface design and RED fixtures may proceed in parallel with #341 Phase 0A–0C; **runtime** Evidence Reconciliation must not merge or activate before **#341 E0** is accepted (see the entry-gates bullet in the 3b contract below). Implement only a thin consumer adapter; do not wait for a generic #320 registry/CDG. Includes the minimum trusted producer for baseline/candidate/config/task-scope/reviewer identity, fail-closed negative cases, doc-governed gating on `step`/`obligation` writes at the verification/completion boundary, and freshness re-check at completion. The #335 baseline-bound deletion/rename evidence has shipped (#342); claiming complete integration still requires the reconciliation gate above. Preserve existing required-binding compatibility |
|
|
19
|
+
| 3c — H1–H4 and paired-task evaluation | **Not started.** Mechanism tests alone are insufficient for architecture promotion; compare frozen baseline and treatment on real bounded tasks, success/failure first, then conditional cost and latency |
|
|
20
|
+
| 1b — shared documentation craft and family metadata | **Splittable.** Shared references and thin skill consumers can proceed independently. A small optional logical-family metadata seam may be piloted; physical skill directory moves remain blocked on #320 identity/path separation |
|
|
21
|
+
| 4 — derived-view generators and `describes:` drift | **Not started.** Preserve generator authorization, deterministic regenerate-equals and scoped reminder semantics |
|
|
22
|
+
|
|
23
|
+
### Phase 3b — narrow integration contract (planned, not shipped)
|
|
24
|
+
|
|
25
|
+
- **Inputs:** explicit active `doc-change` step, D6.5 diff facts, Phase 3a obligations/evidence, available provider *metadata*, and host capabilities.
|
|
26
|
+
- **Entry and activation gates (#341):** parallel preparatory work — provider metadata, adapter/interface design and RED fixtures — may proceed while #341 Phase 0A–0C runs, but the **runtime** Evidence Reconciliation must not merge or activate until **#341 E0** is reviewed and accepted (initial evidence read/binding/outcome conformance, mutation and read-order compatibility, source/package parity, zero false MUST PASS on applicable adversarial cases, and applicable green CI including Windows). After E0, only the **bounded, opt-in, reversible** documentation consumer is eligible for runtime integration at the existing verification/disposition path (no second router, forced provider, new lock, or weakened authorization, with a documented rollback), and it supplies the observed exposure for **#341 E1** (initial target 2–3 genuine development/review cycles with sufficient case mix, not elapsed time). **#341 E1 GO / REVISE / NO-GO gates expansion beyond the limited pilot, not the pilot's own observation** (missing or unrepresentative exposure is `UNCHECKED`, never an automatic GO). The H1–H4 paired-task architectural gate below stays required for any #320 promotion in addition to E1. Shared evidence-read/binding/decision defects route to #341 (fixture-first correction, regression replay, re-observation); documentation policy and the trusted host/VCS producer remain #325 deliverables here.
|
|
27
|
+
- **Advisory provider suggestions:** `suggestDocProviders({activeStep, neededCapabilities, availableProviderMetadata, hostCapabilities})` yields candidate providers and unavailable reasons without altering the MUST/SHOULD obligation set, granting tool access, or eagerly reading unrelated `SKILL.md`. Existing binding resolution handles an explicitly chosen provider. No new automatic step selection.
|
|
28
|
+
- **Evidence reconciliation boundary and credit order:** the pilot wires Phase 3a results into the non-Fable typed-step path only. `hooks/scripts/workflow-disposition.js` delegates `step` to `updateStep()` and `binding` to `updateBinding()` in `hooks/scripts/lib/workflow-obligations.js`. Today `updateStep()` persists `step.status = 'pass'` when every required binding is `loaded` and `--evidence` is non-empty; `syncTypedAggregateObligations()` then derives aggregate `execute`/`verify` PASS from the persisted step statuses. An authored `doc-change` step is authored work, not verification: it may advance under its existing binding contract **without claiming** any doc MUST as verified. The doc-MUST reconciliation gate belongs at the authorized `verification` step and the final-completion boundary, matching D6.6 ("All events are checked at the existing workflow's `verification` step"). Phase 3b must evaluate `checkDocObligations()` for the active scope before aggregate `verify` PASS is persisted and before the workflow reports completion; an aggregate `execute` may still reflect authored step passes, but neither aggregate may stand as completion while an applicable doc MUST is unsatisfied, and `unresolvedObligations()` must surface the unsatisfied results (`pending`/`unknown`/`unavailable`/`failed`). Ordered `doc-change → review → verification` topologies stay reachable: evidence supplied by the later `review`/`verification` steps counts at its own stage, so no stage is forced to pass early and a normative doc change cannot deadlock. Non-document steps, persisted plans, existing bindings and authorization stay unchanged.
|
|
29
|
+
- **Typed aggregate writes and freshness (doc-governed scope):** aggregate PASS is not derived exclusively from persisted step passes today. `updateObligation()` accepts a direct `obligation --obligation-id execute|verify --disposition pass --evidence <text>` write for **all** non-Fable typed plans, including workflows with no applicable document MUSTs. Phase 3b's guard is scoped to typed plans/steps with an applicable, doc-governed scope: there, a direct typed `execute`/`verify` PASS must be rejected or ignored unless it was derived/reconciled from satisfied doc evidence, and the `obligation` command joins `step` in the negative tests. Non-document typed workflows and persisted plans retain today's behavior unless a separately authorized compatibility decision changes them; an already-persisted plan that gains a doc-governed scope is validated against that scope and cannot complete on a stale direct aggregate PASS. Verified doc evidence must also stay bound to the persisted candidate, task and config identity: `unresolvedObligations()` and the final completion path re-check freshness, so evidence that was valid before a later candidate edit cannot remain a completion proof solely because `status: pass` was persisted.
|
|
30
|
+
- **Minimum trusted producer (3b deliverable):** Phase 3b ships the pilot-local adapter that derives the VCS baseline and candidate identity, active task scope, parsed config bytes (`configDigest` bound rather than presence-checked), PRD root, execution/verification provenance and independent reviewer identity from host state (see *Phase 3b minimum trust boundary* below). `checkDocObligations()` only proves agreement with caller-supplied values; without this producer, a 3b PASS would promote caller-attested claims. Any input the producer cannot establish stays `unknown`/`unavailable` and MUST NOT pass. Phase 3c broadens adversarial H3 validation and adds paired architectural evaluation; it does not supply the minimum producer.
|
|
31
|
+
- **Pilot topology and unsupported paths:** the pilot is limited to non-Fable typed-step workflows (`stepBindingsEnabled === true`, set when typed steps exist and the strategy is not `fable-*`). Fable stage-contract paths (`fable-staged`, `fable-parallel`, `fable-multi-agent-workspace`) and workflows without the typed-step contract are explicitly **not integrated / unavailable** for doc-MUST reconciliation: a `stage-binding` disposition is never doc verification, and no fallback may mark a doc MUST satisfied. Unsupported paths report that state rather than silently passing.
|
|
32
|
+
- **Negative acceptance case:** for a doc-governed typed plan whose applicable MUST is failing or unknown, no boundary may report PASS: `step --disposition pass` on the verification step, `obligation --obligation-id execute|verify --disposition pass --evidence 'done'`, and final completion must all fail closed, report the unsatisfied obligation ids, and leave aggregate `verify` pending. A loaded skill plus free-text evidence is not verification. A RED fixture covers ordered `doc-change → review → verification` with reviewer/command evidence missing first and valid later: the authored `doc-change` step advances, aggregate/final PASS stays blocked until the later evidence satisfies the MUST, and neither a premature PASS nor a deadlock occurs.
|
|
33
|
+
- **Compatibility:** preserve existing non-document step bindings, authorization/Rule-of-3 semantics, old persisted plans and no new Stop/tool lock. Source and installed plugin copies must behave consistently.
|
|
34
|
+
- **Pre-integration work:** the baseline-bound tombstone/rename-pair evidence required by #335 shipped in #342; the remaining pre-integration work is the reconciliation contract above. #331 / draft PR #333 is a parallel host-config safety correction; neither requires the broad #320 migration.
|
|
35
|
+
|
|
36
|
+
### Phase 3c — architecture promotion gate for #320 (planned, not shipped)
|
|
37
|
+
|
|
38
|
+
| Hypothesis | Objective acceptance evidence |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| **H1 Provider independence** | Swap/omit suggestions without changing workflow obligations; valid alternate execution evidence can satisfy an applicable MUST; no evidence cannot |
|
|
41
|
+
| **H2 Active-step progressive disclosure** | Captured step output / provider metadata access shows only active-step relevance and no eager unrelated `SKILL.md` content or future-step loading |
|
|
42
|
+
| **H3 Evidence reconciliation** | Adversarial replay of fabricated completion, missing verifier, stale digest/base, scope substitution, forged identity, unsupported PRD roots, contradictory/failed results and unsupported waiver yields **zero false PASS** for MUSTs, with the fixture matrix below as the observable trust boundary |
|
|
43
|
+
| **H4 Task-shaped composition** | Docs-only, docs+code, REQ-citing assertions, typo-only changes, code-only uncertainty, missing independent approval and waivers produce the expected obligation union and explicit unknown/unavailable outcomes |
|
|
44
|
+
|
|
45
|
+
### Phase 3b minimum trust boundary and Phase 3c H3 validation (planned, not shipped)
|
|
46
|
+
|
|
47
|
+
Phase 3b delivers the minimum producer and identity/scope binding needed for a trustworthy PASS; Phase 3c extends the same seam with adversarial H3 validation and paired architectural evaluation.
|
|
48
|
+
|
|
49
|
+
- **Trusted producer, not the checker:** a pilot-local adapter is a **3b deliverable**; it derives the VCS baseline and candidate identity, active task scope, project-docs config identity (parsed config bytes, not presence) and execution/review provenance from host state. `checkDocObligations()` proves only that records agree with those caller-supplied values; matching a caller-provided digest is not independent validation of the input.
|
|
50
|
+
- **Identity and scope requirements:** `baseline.digest` and `baseline.artifacts[path]` come from the same recorded baseline tree; candidate artifact digests come from the candidate tree actually under review; `configDigest` is bound to the parsed config bytes rather than checked for presence; the obligation's PRD root and active task scope must match the adapter's scope, and an unsupported custom PRD root is `unavailable`.
|
|
51
|
+
- **Provenance and reviewer identity:** execution and verification records need the command, non-empty provenance and integer exit code; review approval needs an independent reviewer identity distinct from the executor/author. A free-text claim, self-review, missing verifier or replayed record never satisfies a MUST.
|
|
52
|
+
- **Dispositions:** absent, untrusted, stale, replayed, contradictory, malformed or unsupported inputs resolve to `unknown`/`unavailable`/failed, never PASS. The 3b fail-closed negative cases cover missing producer identity, scope substitution, unbound config bytes and stale candidate evidence; 3c adds the full adversarial fixture matrix below.
|
|
53
|
+
|
|
54
|
+
| H3 fixture | Expected disposition |
|
|
10
55
|
| --- | --- |
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
56
|
+
| Fabricated completion claim / no record | `unknown`; MUST unsatisfied |
|
|
57
|
+
| Forged baseline or candidate digest | rejected as stale/unknown; MUST unsatisfied |
|
|
58
|
+
| Scope substitution across tasks or PRD roots | `unknown`; MUST unsatisfied |
|
|
59
|
+
| Unsupported custom PRD root | `unavailable`; MUST unsatisfied |
|
|
60
|
+
| Replayed earlier-candidate record | stale; MUST unsatisfied |
|
|
61
|
+
| Missing, non-independent or mismatched reviewer identity | MUST unsatisfied |
|
|
62
|
+
| Unsupported waiver (no reason, no cited observed record, or self-bound scope) | `unsupported-waiver`; MUST unsatisfied |
|
|
63
|
+
| Contradictory or failed command results | failed; not PASS |
|
|
64
|
+
| Positive control: current, independently produced matching evidence | pass |
|
|
65
|
+
|
|
66
|
+
Two *separate* gates apply. The **functional gate** requires contract tests, negative cases, source/package parity, host/platform compatibility and an all-green applicable CI baseline. The **architectural gate** additionally requires H1–H4 and independently reviewed paired real-task outcomes. Freeze task fixtures, acceptance rubrics, model/effort, host permissions, comparison order and thresholds **after baseline measurement but before treatment outcomes**. Compare task correctness and failures first; report successful-task token/time/tool cost and failed-run overhead separately, with uncertainty. A small inconclusive sample is not approval. Broader #320 CDG/registry compilation and physical skill moves need **explicit owner sign-off on each specific validated interface**; retain the legacy path until then.
|
|
67
|
+
|
|
68
|
+
**Cross-contract acceptance assertion:** the #341 E0/E1 gates in the Phase 3b entry-gates bullet above, [#341](https://github.com/dyphn1/Harness-everything/issues/341) and #325's phase table must stay mutually consistent: a change to one gate's semantics updates the other two records, replays affected #341 fixtures first, and only then re-collects affected 3b observations. E0 enables only the bounded, reversible, doc-only runtime consumer; E1 gates expansion beyond the limited pilot; neither approves generic #320 CDG/registry promotion, which additionally requires the H1–H4 paired real-task evidence and explicit owner sign-off above.
|
|
17
69
|
|
|
18
70
|
## Configuration (D5)
|
|
19
71
|
|
|
@@ -78,6 +78,25 @@ function isPlainObject(value) {
|
|
|
78
78
|
return prototype === Object.prototype || prototype === null;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
function normalizeHostProjectDocs(projectDocs) {
|
|
82
|
+
if (!isPlainObject(projectDocs)) return { projectDocs, warnings: [] };
|
|
83
|
+
|
|
84
|
+
const normalized = { ...projectDocs };
|
|
85
|
+
const warnings = [];
|
|
86
|
+
if (typeof normalized.domains === 'string') {
|
|
87
|
+
if (typeof normalized.domainLocation !== 'string' || !normalized.domainLocation.trim()) {
|
|
88
|
+
normalized.domainLocation = normalized.domains;
|
|
89
|
+
}
|
|
90
|
+
delete normalized.domains;
|
|
91
|
+
warnings.push({
|
|
92
|
+
code: 'deprecated-domains-string',
|
|
93
|
+
severity: 'warning',
|
|
94
|
+
message: 'host projectDocs.domains as a string is deprecated; use domainLocation for the zone path and domains as the ID-prefix array'
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return { projectDocs: normalized, warnings };
|
|
98
|
+
}
|
|
99
|
+
|
|
81
100
|
function projectDocsConfigPath(workspaceRoot) {
|
|
82
101
|
return path.join(workspaceRoot, PROJECT_DOCS_CONFIG);
|
|
83
102
|
}
|
|
@@ -148,22 +167,12 @@ function inspectHostProjectDocs(workspaceRoot) {
|
|
|
148
167
|
continue;
|
|
149
168
|
}
|
|
150
169
|
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
if (typeof projectDocs.domainLocation !== 'string' || !projectDocs.domainLocation.trim()) {
|
|
154
|
-
projectDocs.domainLocation = projectDocs.domains;
|
|
155
|
-
}
|
|
156
|
-
delete projectDocs.domains;
|
|
157
|
-
entry.warnings.push({
|
|
158
|
-
code: 'deprecated-domains-string',
|
|
159
|
-
severity: 'warning',
|
|
160
|
-
path: configSource(workspaceRoot, file),
|
|
161
|
-
message: 'host projectDocs.domains as a string is deprecated; use domainLocation for the zone path and domains as the ID-prefix array'
|
|
162
|
-
});
|
|
163
|
-
}
|
|
170
|
+
const normalizedHost = normalizeHostProjectDocs(manifest.projectDocs);
|
|
171
|
+
const projectDocs = normalizedHost.projectDocs;
|
|
164
172
|
|
|
165
173
|
const validation = validateDocsConfig(projectDocs);
|
|
166
174
|
const source = configSource(workspaceRoot, file);
|
|
175
|
+
entry.warnings.push(...normalizedHost.warnings.map(item => ({ ...item, path: source })));
|
|
167
176
|
entry.warnings.push(...validation.warnings.map(item => ({ ...item, path: source })));
|
|
168
177
|
if (!validation.valid) {
|
|
169
178
|
const details = validation.diagnostics.map(item => item.message).join('; ');
|
|
@@ -236,6 +245,13 @@ function validateDocsConfig(config) {
|
|
|
236
245
|
return { valid: false, diagnostics, warnings };
|
|
237
246
|
}
|
|
238
247
|
|
|
248
|
+
for (const field of ['docLocation', 'tracker', 'issueDefinition']) {
|
|
249
|
+
if (config[field] !== undefined && typeof config[field] !== 'string') {
|
|
250
|
+
const diagnosticField = field.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);
|
|
251
|
+
invalid(`invalid-${diagnosticField}-type`, `${field} must be a string when provided`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
239
255
|
for (const key of Object.keys(config).sort()) {
|
|
240
256
|
if (!PROJECT_DOC_CONFIG_KEYS.has(key)) {
|
|
241
257
|
warnings.push({
|
|
@@ -754,6 +770,7 @@ module.exports = {
|
|
|
754
770
|
readProjectDocs,
|
|
755
771
|
resolveDocsConfig,
|
|
756
772
|
validateDocsConfig,
|
|
773
|
+
normalizeHostProjectDocs,
|
|
757
774
|
parseContextMap,
|
|
758
775
|
resolveProjectDocs,
|
|
759
776
|
detectProjectDocsConventions,
|
|
@@ -78,6 +78,25 @@ function isPlainObject(value) {
|
|
|
78
78
|
return prototype === Object.prototype || prototype === null;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
function normalizeHostProjectDocs(projectDocs) {
|
|
82
|
+
if (!isPlainObject(projectDocs)) return { projectDocs, warnings: [] };
|
|
83
|
+
|
|
84
|
+
const normalized = { ...projectDocs };
|
|
85
|
+
const warnings = [];
|
|
86
|
+
if (typeof normalized.domains === 'string') {
|
|
87
|
+
if (typeof normalized.domainLocation !== 'string' || !normalized.domainLocation.trim()) {
|
|
88
|
+
normalized.domainLocation = normalized.domains;
|
|
89
|
+
}
|
|
90
|
+
delete normalized.domains;
|
|
91
|
+
warnings.push({
|
|
92
|
+
code: 'deprecated-domains-string',
|
|
93
|
+
severity: 'warning',
|
|
94
|
+
message: 'host projectDocs.domains as a string is deprecated; use domainLocation for the zone path and domains as the ID-prefix array'
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return { projectDocs: normalized, warnings };
|
|
98
|
+
}
|
|
99
|
+
|
|
81
100
|
function projectDocsConfigPath(workspaceRoot) {
|
|
82
101
|
return path.join(workspaceRoot, PROJECT_DOCS_CONFIG);
|
|
83
102
|
}
|
|
@@ -148,22 +167,12 @@ function inspectHostProjectDocs(workspaceRoot) {
|
|
|
148
167
|
continue;
|
|
149
168
|
}
|
|
150
169
|
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
if (typeof projectDocs.domainLocation !== 'string' || !projectDocs.domainLocation.trim()) {
|
|
154
|
-
projectDocs.domainLocation = projectDocs.domains;
|
|
155
|
-
}
|
|
156
|
-
delete projectDocs.domains;
|
|
157
|
-
entry.warnings.push({
|
|
158
|
-
code: 'deprecated-domains-string',
|
|
159
|
-
severity: 'warning',
|
|
160
|
-
path: configSource(workspaceRoot, file),
|
|
161
|
-
message: 'host projectDocs.domains as a string is deprecated; use domainLocation for the zone path and domains as the ID-prefix array'
|
|
162
|
-
});
|
|
163
|
-
}
|
|
170
|
+
const normalizedHost = normalizeHostProjectDocs(manifest.projectDocs);
|
|
171
|
+
const projectDocs = normalizedHost.projectDocs;
|
|
164
172
|
|
|
165
173
|
const validation = validateDocsConfig(projectDocs);
|
|
166
174
|
const source = configSource(workspaceRoot, file);
|
|
175
|
+
entry.warnings.push(...normalizedHost.warnings.map(item => ({ ...item, path: source })));
|
|
167
176
|
entry.warnings.push(...validation.warnings.map(item => ({ ...item, path: source })));
|
|
168
177
|
if (!validation.valid) {
|
|
169
178
|
const details = validation.diagnostics.map(item => item.message).join('; ');
|
|
@@ -236,6 +245,13 @@ function validateDocsConfig(config) {
|
|
|
236
245
|
return { valid: false, diagnostics, warnings };
|
|
237
246
|
}
|
|
238
247
|
|
|
248
|
+
for (const field of ['docLocation', 'tracker', 'issueDefinition']) {
|
|
249
|
+
if (config[field] !== undefined && typeof config[field] !== 'string') {
|
|
250
|
+
const diagnosticField = field.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);
|
|
251
|
+
invalid(`invalid-${diagnosticField}-type`, `${field} must be a string when provided`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
239
255
|
for (const key of Object.keys(config).sort()) {
|
|
240
256
|
if (!PROJECT_DOC_CONFIG_KEYS.has(key)) {
|
|
241
257
|
warnings.push({
|
|
@@ -754,6 +770,7 @@ module.exports = {
|
|
|
754
770
|
readProjectDocs,
|
|
755
771
|
resolveDocsConfig,
|
|
756
772
|
validateDocsConfig,
|
|
773
|
+
normalizeHostProjectDocs,
|
|
757
774
|
parseContextMap,
|
|
758
775
|
resolveProjectDocs,
|
|
759
776
|
detectProjectDocsConventions,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "harness-everything",
|
|
4
|
-
"version": "0.36.
|
|
4
|
+
"version": "0.36.3",
|
|
5
5
|
"description": "Invariant-first engineering harness for routing, verification, and failure recovery while preserving agent autonomy.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Miya Daniel",
|
package/plugins/harness-everything/skills/harness-everything/scripts/project-docs-resolver.js
CHANGED
|
@@ -78,6 +78,25 @@ function isPlainObject(value) {
|
|
|
78
78
|
return prototype === Object.prototype || prototype === null;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
function normalizeHostProjectDocs(projectDocs) {
|
|
82
|
+
if (!isPlainObject(projectDocs)) return { projectDocs, warnings: [] };
|
|
83
|
+
|
|
84
|
+
const normalized = { ...projectDocs };
|
|
85
|
+
const warnings = [];
|
|
86
|
+
if (typeof normalized.domains === 'string') {
|
|
87
|
+
if (typeof normalized.domainLocation !== 'string' || !normalized.domainLocation.trim()) {
|
|
88
|
+
normalized.domainLocation = normalized.domains;
|
|
89
|
+
}
|
|
90
|
+
delete normalized.domains;
|
|
91
|
+
warnings.push({
|
|
92
|
+
code: 'deprecated-domains-string',
|
|
93
|
+
severity: 'warning',
|
|
94
|
+
message: 'host projectDocs.domains as a string is deprecated; use domainLocation for the zone path and domains as the ID-prefix array'
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return { projectDocs: normalized, warnings };
|
|
98
|
+
}
|
|
99
|
+
|
|
81
100
|
function projectDocsConfigPath(workspaceRoot) {
|
|
82
101
|
return path.join(workspaceRoot, PROJECT_DOCS_CONFIG);
|
|
83
102
|
}
|
|
@@ -148,22 +167,12 @@ function inspectHostProjectDocs(workspaceRoot) {
|
|
|
148
167
|
continue;
|
|
149
168
|
}
|
|
150
169
|
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
if (typeof projectDocs.domainLocation !== 'string' || !projectDocs.domainLocation.trim()) {
|
|
154
|
-
projectDocs.domainLocation = projectDocs.domains;
|
|
155
|
-
}
|
|
156
|
-
delete projectDocs.domains;
|
|
157
|
-
entry.warnings.push({
|
|
158
|
-
code: 'deprecated-domains-string',
|
|
159
|
-
severity: 'warning',
|
|
160
|
-
path: configSource(workspaceRoot, file),
|
|
161
|
-
message: 'host projectDocs.domains as a string is deprecated; use domainLocation for the zone path and domains as the ID-prefix array'
|
|
162
|
-
});
|
|
163
|
-
}
|
|
170
|
+
const normalizedHost = normalizeHostProjectDocs(manifest.projectDocs);
|
|
171
|
+
const projectDocs = normalizedHost.projectDocs;
|
|
164
172
|
|
|
165
173
|
const validation = validateDocsConfig(projectDocs);
|
|
166
174
|
const source = configSource(workspaceRoot, file);
|
|
175
|
+
entry.warnings.push(...normalizedHost.warnings.map(item => ({ ...item, path: source })));
|
|
167
176
|
entry.warnings.push(...validation.warnings.map(item => ({ ...item, path: source })));
|
|
168
177
|
if (!validation.valid) {
|
|
169
178
|
const details = validation.diagnostics.map(item => item.message).join('; ');
|
|
@@ -236,6 +245,13 @@ function validateDocsConfig(config) {
|
|
|
236
245
|
return { valid: false, diagnostics, warnings };
|
|
237
246
|
}
|
|
238
247
|
|
|
248
|
+
for (const field of ['docLocation', 'tracker', 'issueDefinition']) {
|
|
249
|
+
if (config[field] !== undefined && typeof config[field] !== 'string') {
|
|
250
|
+
const diagnosticField = field.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);
|
|
251
|
+
invalid(`invalid-${diagnosticField}-type`, `${field} must be a string when provided`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
239
255
|
for (const key of Object.keys(config).sort()) {
|
|
240
256
|
if (!PROJECT_DOC_CONFIG_KEYS.has(key)) {
|
|
241
257
|
warnings.push({
|
|
@@ -754,6 +770,7 @@ module.exports = {
|
|
|
754
770
|
readProjectDocs,
|
|
755
771
|
resolveDocsConfig,
|
|
756
772
|
validateDocsConfig,
|
|
773
|
+
normalizeHostProjectDocs,
|
|
757
774
|
parseContextMap,
|
|
758
775
|
resolveProjectDocs,
|
|
759
776
|
detectProjectDocsConventions,
|
package/plugins/harness-everything/skills/multi-agent-workspace/scripts/project-docs-resolver.js
CHANGED
|
@@ -78,6 +78,25 @@ function isPlainObject(value) {
|
|
|
78
78
|
return prototype === Object.prototype || prototype === null;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
function normalizeHostProjectDocs(projectDocs) {
|
|
82
|
+
if (!isPlainObject(projectDocs)) return { projectDocs, warnings: [] };
|
|
83
|
+
|
|
84
|
+
const normalized = { ...projectDocs };
|
|
85
|
+
const warnings = [];
|
|
86
|
+
if (typeof normalized.domains === 'string') {
|
|
87
|
+
if (typeof normalized.domainLocation !== 'string' || !normalized.domainLocation.trim()) {
|
|
88
|
+
normalized.domainLocation = normalized.domains;
|
|
89
|
+
}
|
|
90
|
+
delete normalized.domains;
|
|
91
|
+
warnings.push({
|
|
92
|
+
code: 'deprecated-domains-string',
|
|
93
|
+
severity: 'warning',
|
|
94
|
+
message: 'host projectDocs.domains as a string is deprecated; use domainLocation for the zone path and domains as the ID-prefix array'
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return { projectDocs: normalized, warnings };
|
|
98
|
+
}
|
|
99
|
+
|
|
81
100
|
function projectDocsConfigPath(workspaceRoot) {
|
|
82
101
|
return path.join(workspaceRoot, PROJECT_DOCS_CONFIG);
|
|
83
102
|
}
|
|
@@ -148,22 +167,12 @@ function inspectHostProjectDocs(workspaceRoot) {
|
|
|
148
167
|
continue;
|
|
149
168
|
}
|
|
150
169
|
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
if (typeof projectDocs.domainLocation !== 'string' || !projectDocs.domainLocation.trim()) {
|
|
154
|
-
projectDocs.domainLocation = projectDocs.domains;
|
|
155
|
-
}
|
|
156
|
-
delete projectDocs.domains;
|
|
157
|
-
entry.warnings.push({
|
|
158
|
-
code: 'deprecated-domains-string',
|
|
159
|
-
severity: 'warning',
|
|
160
|
-
path: configSource(workspaceRoot, file),
|
|
161
|
-
message: 'host projectDocs.domains as a string is deprecated; use domainLocation for the zone path and domains as the ID-prefix array'
|
|
162
|
-
});
|
|
163
|
-
}
|
|
170
|
+
const normalizedHost = normalizeHostProjectDocs(manifest.projectDocs);
|
|
171
|
+
const projectDocs = normalizedHost.projectDocs;
|
|
164
172
|
|
|
165
173
|
const validation = validateDocsConfig(projectDocs);
|
|
166
174
|
const source = configSource(workspaceRoot, file);
|
|
175
|
+
entry.warnings.push(...normalizedHost.warnings.map(item => ({ ...item, path: source })));
|
|
167
176
|
entry.warnings.push(...validation.warnings.map(item => ({ ...item, path: source })));
|
|
168
177
|
if (!validation.valid) {
|
|
169
178
|
const details = validation.diagnostics.map(item => item.message).join('; ');
|
|
@@ -236,6 +245,13 @@ function validateDocsConfig(config) {
|
|
|
236
245
|
return { valid: false, diagnostics, warnings };
|
|
237
246
|
}
|
|
238
247
|
|
|
248
|
+
for (const field of ['docLocation', 'tracker', 'issueDefinition']) {
|
|
249
|
+
if (config[field] !== undefined && typeof config[field] !== 'string') {
|
|
250
|
+
const diagnosticField = field.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);
|
|
251
|
+
invalid(`invalid-${diagnosticField}-type`, `${field} must be a string when provided`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
239
255
|
for (const key of Object.keys(config).sort()) {
|
|
240
256
|
if (!PROJECT_DOC_CONFIG_KEYS.has(key)) {
|
|
241
257
|
warnings.push({
|
|
@@ -754,6 +770,7 @@ module.exports = {
|
|
|
754
770
|
readProjectDocs,
|
|
755
771
|
resolveDocsConfig,
|
|
756
772
|
validateDocsConfig,
|
|
773
|
+
normalizeHostProjectDocs,
|
|
757
774
|
parseContextMap,
|
|
758
775
|
resolveProjectDocs,
|
|
759
776
|
detectProjectDocsConventions,
|