freeharness 0.4.1 → 0.4.2
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/README.md +8 -7
- package/canonical/HARVEST.yaml +25 -14
- package/canonical/PROVENANCE.yaml +1 -1
- package/canonical/agents/README.md +77 -36
- package/canonical/agents/agentic-architect.md +2 -1
- package/canonical/agents/database-reviewer.md +6 -8
- package/canonical/agents/explorer-ai-engineer-analyst.md +2 -6
- package/canonical/agents/implementer.md +30 -0
- package/canonical/agents/pr-reviewer.md +9 -11
- package/canonical/agents/python-reviewer.md +7 -9
- package/canonical/agents/report-formatter.md +7 -3
- package/canonical/agents/security-reviewer.md +7 -8
- package/canonical/agents/test-writer.md +5 -2
- package/canonical/hooks/cost-prices.json +11 -9
- package/canonical/hooks/freeharness-config-guard.mjs +8072 -87
- package/canonical/hooks/freeharness-context-monitor.mjs +88 -54
- package/canonical/hooks/freeharness-converge.mjs +78 -23
- package/canonical/hooks/freeharness-cost-tracker.mjs +1129 -218
- package/canonical/hooks/freeharness-memory-guard.mjs +20 -23
- package/canonical/hooks/freeharness-precompact.mjs +217 -87
- package/canonical/hooks/freeharness-quota-tap.mjs +173 -0
- package/canonical/hooks/freeharness-reload-context.mjs +197 -0
- package/canonical/hooks/freeharness-repeat-tool.mjs +1 -1
- package/canonical/hooks/freeharness-sdd-context.mjs +970 -0
- package/canonical/hooks/freeharness-session-start +297 -59
- package/canonical/hooks/hooks.claude-code.json +1 -1
- package/canonical/hooks/hooks.config-guard.claude-code.json +3 -2
- package/canonical/hooks/hooks.context-monitor.claude-code.json +1 -1
- package/canonical/hooks/hooks.converge.claude-code.json +1 -1
- package/canonical/hooks/hooks.memory-guard.claude-code.json +3 -2
- package/canonical/hooks/hooks.reload-context.claude-code.json +15 -0
- package/canonical/openspec/schema.spec-driven.yaml +29 -8
- package/canonical/openspec/templates/follow-ups.md +3 -2
- package/canonical/openspec/templates/proposal.md +1 -1
- package/canonical/openspec/templates/tasks.md +8 -0
- package/canonical/rules/CLAUDE.md +5 -4
- package/canonical/rules/routing.md +10 -9
- package/canonical/skills/agent-evaluation/SKILL.md +17 -6
- package/canonical/skills/agent-evaluation/evals/cases.yaml +38 -23
- package/canonical/skills/agentic-architecture/SKILL.md +4 -0
- package/canonical/skills/agentic-architecture/evals/cases.yaml +27 -0
- package/canonical/skills/code-review/SKILL.md +9 -2
- package/canonical/skills/code-review/evals/cases.yaml +1118 -27
- package/canonical/skills/git-workflow/SKILL.md +2 -0
- package/canonical/skills/local-code/SKILL.md +2 -2
- package/canonical/skills/local-code/evals/cases.yaml +1 -1
- package/canonical/skills/report-formatting/SKILL.md +10 -2
- package/canonical/skills/report-formatting/reference/ARCHITECTURE.md +3 -3
- package/canonical/skills/report-formatting/reference/HELPERS.md +10 -5
- package/canonical/skills/report-formatting/reference/RELEASE.md +1 -2
- package/canonical/skills/report-formatting/reference/TEMPLATES.md +3 -2
- package/canonical/skills/report-formatting/reference/authoring.md +25 -37
- package/canonical/skills/report-formatting/scripts/build-architecture.js +1 -1
- package/canonical/skills/report-formatting/scripts/build-md-claro.js +5 -8
- package/canonical/skills/report-formatting/scripts/check-env.js +0 -4
- package/canonical/skills/report-formatting/scripts/docmodel.js +3 -11
- package/canonical/skills/report-formatting/scripts/example-build.js +8 -10
- package/canonical/skills/report-formatting/scripts/example_build_ArchitectureReview_docx.js +8 -10
- package/canonical/skills/report-formatting/scripts/example_build_ExecutiveReport_docx.js +8 -10
- package/canonical/skills/report-formatting/scripts/theme-claro.js +39 -10
- package/canonical/skills/subagent-driven-development/SKILL.md +35 -28
- package/canonical/skills/subagent-driven-development/evals/cases.yaml +3496 -19
- package/canonical/skills/subagent-driven-development/reference/brief.md +185 -35
- package/canonical/skills/subagent-driven-development/reference/handoff.md +82 -2
- package/canonical/skills/subagent-driven-development/reference/loop.md +57 -0
- package/canonical/skills/subagent-driven-development/reference/trees.md +40 -0
- package/canonical/skills/subagent-driven-development/reference/workspace.md +31 -0
- package/canonical/skills/subagent-driven-development/scripts/validate-handoff.mjs +4771 -326
- package/dist/adapters/harness/claude-code.d.ts +19 -3
- package/dist/adapters/harness/claude-code.js +204 -39
- package/dist/adapters/harness/opencode.js +16 -2
- package/dist/adapters/harness/plugin.js +28 -17
- package/dist/adapters/memory/doctor.d.ts +174 -29
- package/dist/adapters/memory/doctor.js +758 -134
- package/dist/adapters/memory/migrate.d.ts +95 -0
- package/dist/adapters/memory/migrate.js +497 -0
- package/dist/adapters/memory/paths.d.ts +22 -0
- package/dist/adapters/memory/paths.js +85 -0
- package/dist/adapters/memory/vendored.d.ts +56 -29
- package/dist/adapters/memory/vendored.js +191 -154
- package/dist/adapters/sync/diff.js +4 -1
- package/dist/cli/cleanup-prompt.d.ts +35 -0
- package/dist/cli/cleanup-prompt.js +68 -0
- package/dist/cli/cleanup.d.ts +22 -24
- package/dist/cli/cleanup.js +182 -68
- package/dist/cli/cost-quota.d.ts +158 -0
- package/dist/cli/cost-quota.js +645 -0
- package/dist/cli/cost.d.ts +408 -46
- package/dist/cli/cost.js +1731 -57
- package/dist/cli/doctor.d.ts +52 -13
- package/dist/cli/doctor.js +193 -44
- package/dist/cli/hard-cleanup.d.ts +93 -0
- package/dist/cli/hard-cleanup.js +552 -0
- package/dist/cli/install-facts.d.ts +28 -0
- package/dist/cli/install-facts.js +66 -0
- package/dist/cli/json-key-edit.d.ts +18 -0
- package/dist/cli/json-key-edit.js +118 -0
- package/dist/cli/memory-import.d.ts +74 -0
- package/dist/cli/memory-import.js +469 -0
- package/dist/cli/plugin-validate.d.ts +12 -0
- package/dist/cli/plugin-validate.js +43 -0
- package/dist/cli/uninstall.d.ts +1 -1
- package/dist/cli/uninstall.js +2 -2
- package/dist/cli/upstream-launchers.d.ts +49 -0
- package/dist/cli/upstream-launchers.js +287 -0
- package/dist/index.js +247 -70
- package/dist/infra/fsutil.d.ts +31 -3
- package/dist/infra/fsutil.js +69 -6
- package/dist/infra/manifest.d.ts +2 -1
- package/dist/infra/manifest.js +3 -2
- package/dist/infra/prices.d.ts +18 -0
- package/dist/infra/prices.js +64 -0
- package/node_modules/@freeharness/core/dist/tiers.d.ts +10 -1
- package/node_modules/@freeharness/core/dist/tiers.js +24 -2
- package/node_modules/@freeharness/core/package.json +1 -1
- package/node_modules/@freeharness/sdd/PROVENANCE.yaml +279 -2
- package/node_modules/@freeharness/sdd/dist/src/bin/openspec.js +34 -8
- package/node_modules/@freeharness/sdd/dist/src/shim/archive.d.ts +7 -6
- package/node_modules/@freeharness/sdd/dist/src/shim/archive.js +72 -64
- package/node_modules/@freeharness/sdd/dist/src/shim/follow-up.d.ts +48 -16
- package/node_modules/@freeharness/sdd/dist/src/shim/follow-up.js +168 -70
- package/node_modules/@freeharness/sdd/dist/src/shim/instructions.d.ts +36 -6
- package/node_modules/@freeharness/sdd/dist/src/shim/instructions.js +110 -49
- package/node_modules/@freeharness/sdd/dist/src/shim/list.d.ts +1 -1
- package/node_modules/@freeharness/sdd/dist/src/shim/list.js +57 -14
- package/node_modules/@freeharness/sdd/dist/src/shim/nested-change.d.ts +7 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/nested-change.js +118 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/new-change.js +3 -1
- package/node_modules/@freeharness/sdd/dist/src/shim/proposal-capabilities.d.ts +12 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/proposal-capabilities.js +94 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/shared.d.ts +17 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/shared.js +182 -1
- package/node_modules/@freeharness/sdd/dist/src/shim/status.d.ts +2 -2
- package/node_modules/@freeharness/sdd/dist/src/shim/status.js +34 -5
- package/node_modules/@freeharness/sdd/dist/src/shim/validate.d.ts +9 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/validate.js +49 -41
- package/node_modules/@freeharness/sdd/dist/src/shim/warnings.d.ts +21 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/warnings.js +66 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/line-endings.d.ts +4 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/line-endings.js +17 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/specs-apply.js +95 -22
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-status-policy.d.ts +6 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-status-policy.js +14 -6
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/index.d.ts +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/index.js +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/outputs.d.ts +3 -3
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/outputs.js +24 -4
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/task-progress.js +4 -7
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/types.js +13 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/apply-change.js +21 -6
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/archive-change.js +80 -19
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/explore.js +42 -22
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/project-root.d.ts +32 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/project-root.js +39 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/propose.js +16 -9
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/sync-specs.js +36 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/update-change.js +65 -30
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/constants.d.ts +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/constants.js +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/change-parser.d.ts +8 -13
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/change-parser.js +79 -102
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/markdown-parser.js +4 -9
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.d.ts +50 -10
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.js +165 -37
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.d.ts +24 -3
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.js +55 -8
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/spec-structure.js +4 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/purpose-placeholder.js +19 -9
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/spec-discovery.d.ts +10 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/spec-discovery.js +115 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-lines.d.ts +3 -3
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-lines.js +20 -20
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/validator.d.ts +6 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/validator.js +75 -14
- package/node_modules/@freeharness/sdd/package.json +1 -1
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/schema.upstream.yaml +47 -7
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-apply-change/SKILL.md +25 -7
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-archive-change/SKILL.md +50 -11
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-bulk-archive-change/SKILL.md +48 -10
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-continue-change/SKILL.md +27 -7
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-explore/SKILL.md +25 -12
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-ff-change/SKILL.md +17 -5
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-new-change/SKILL.md +12 -1
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-onboard/SKILL.md +47 -33
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-propose/SKILL.md +17 -5
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-sync-specs/SKILL.md +29 -1
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-update-change/SKILL.md +31 -13
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-verify-change/SKILL.md +75 -27
- package/package.json +6 -4
- package/provenance/canonical.PROVENANCE.yaml +1 -1
- package/provenance/memory.PROVENANCE.yaml +458 -6
- package/provenance/sdd.PROVENANCE.yaml +279 -2
- package/canonical/hooks/freeharness-auto-memory.mjs +0 -158
- package/canonical/hooks/hooks.auto-memory.claude-code.json +0 -16
|
@@ -4,8 +4,8 @@ upstream:
|
|
|
4
4
|
name: "@fission-ai/openspec"
|
|
5
5
|
repo: https://github.com/Fission-AI/OpenSpec
|
|
6
6
|
fork: https://github.com/anegrelli/OpenSpec
|
|
7
|
-
baseline_version: "1.
|
|
8
|
-
baseline_commit: "
|
|
7
|
+
baseline_version: "1.15.0"
|
|
8
|
+
baseline_commit: "6d04a1b5"
|
|
9
9
|
|
|
10
10
|
vendored:
|
|
11
11
|
assets:
|
|
@@ -83,6 +83,8 @@ patches:
|
|
|
83
83
|
- "task-progress.ts disposition kinds: `countTasksFromContent` resolves a task only for the documented kinds (superseded, moved, external); any other kind leaves the task open, matching the converge hook and the ERROR `validate --strict` reports for it"
|
|
84
84
|
- "formatTaskStatus (task-progress.ts): accepts the optional disposition-aware counts, reporting a change complete when `open` is zero and naming the dispositioned count while tasks are still open; a caller passing only `total`/`completed` keeps the upstream reading"
|
|
85
85
|
- "shim disposition rules (validate/status/archive, FreeHarness addition): validate.ts adds an ERROR for a disposition under a checked task, an unknown kind, an unresolved `superseded`/`moved` target, or an empty `external` reference; status.ts exposes `done`/`open`/`dispositioned` in `--json`, with the tasks resolved (never open) exactly when `open` is zero; archive.ts excludes a dispositioned task from the unchecked-task blocker and prints the dispositioned tasks grouped by kind after the other archive output"
|
|
86
|
+
- "spec-discovery.ts (divergence from the #1870 port of src/utils/spec-discovery.ts, see ports_1_13_2): findUnreadDeltaFiles and findUnreadDeltaFilesSync drive one generator walker (walkUnreadDeltaFiles) that yields each readdir/stat/read call, so the async and sync twins share one set of walk rules and return the same list; the results are unchanged"
|
|
87
|
+
- "specs-apply.ts comment: the RENAMED-vs-REMOVED conflict comment in buildUpdatedSpec is restated as a present-tense counterfactual (a missing REMOVED target is a no-op, so without the explicit rejection the REMOVED silently does nothing); code unchanged"
|
|
86
88
|
|
|
87
89
|
skipped:
|
|
88
90
|
- "telemetry (PostHog); store/worksets; shell completions; version-check/self-update"
|
|
@@ -167,7 +169,282 @@ ports_1_13_ignored:
|
|
|
167
169
|
- pr: "#1782"
|
|
168
170
|
note: "retire_capabilities — capability retirement (retire_capabilities marker + retireSpec deletion path) was not reconstructed in P3 inc.4c's archive shim; retireSpec/finalizeRetiredSpec are vendored in specs-apply.ts but unused by the shim orchestration"
|
|
169
171
|
|
|
172
|
+
# vendor-sync-2026-10 ports from upstream 9d4e5974 (1.13.0) -> db230978 (1.13.2, MIT).
|
|
173
|
+
# Each entry names the upstream file(s), the PR, and the vendored target;
|
|
174
|
+
# `sync adopt sdd` never touches these (tracked assets only).
|
|
175
|
+
ports_1_13_2:
|
|
176
|
+
- upstream: src/core/templates/workflows/explore.ts, src/core/templates/workflows/update-change.ts
|
|
177
|
+
pr: "#1731"
|
|
178
|
+
target: vendor/openspec/generation/templates/workflows/{explore,update-change}.ts
|
|
179
|
+
note: "explore and update-change describe an `openspec list --json` entry by name and task status only; the schema field this text once cited is gone from that output"
|
|
180
|
+
- upstream: src/core/templates/workflows/archive-change.ts
|
|
181
|
+
pr: "#1795"
|
|
182
|
+
target: vendor/openspec/generation/templates/workflows/archive-change.ts
|
|
183
|
+
note: "archive-change counts incomplete tasks from the selected change's `totalTasks`/`completedTasks` in `openspec list --json` rather than reading tasks.md directly, so a schema's custom-named or glob task files are counted; a missing or duplicate change entry, invalid JSON, or an inconsistent count stops the workflow before syncing or archiving, and a zero `totalTasks` skips the warning instead of a missing tasks.md doing so"
|
|
184
|
+
- upstream: src/core/templates/workflows/update-change.ts
|
|
185
|
+
pr: "#1733"
|
|
186
|
+
target: vendor/openspec/generation/templates/workflows/update-change.ts
|
|
187
|
+
note: "update-change treats a `skipped` artifact as untouched rather than missing, defers only a `ready`/`blocked` artifact with empty `existingOutputPaths` to the continue workflow, and adds a confirmed, re-verified procedure (fresh instructions, a path inside changeRoot that does not already exist, a create that fails on collision) for adding one concrete file under a glob artifact that already has other files"
|
|
188
|
+
- upstream: schemas/spec-driven/schema.yaml
|
|
189
|
+
pr: "#1955"
|
|
190
|
+
target: "canonical/openspec/schema.spec-driven.yaml (override) + assets/schema.upstream.yaml"
|
|
191
|
+
note: "the tasks instruction requires each task group to land the tests and documentation its own work calls for, reserving a final group for integration checks only, so a group's deferred verification never cascades failures back through the groups after it; FreeHarness addition: the final group may also hold the whole-change gates (full suite, simulate, matrix coverage, baseline bumps)"
|
|
192
|
+
- upstream: src/core/parsers/requirement-blocks.ts, src/core/specs-apply.ts, src/core/validation/validator.ts
|
|
193
|
+
pr: "#1809"
|
|
194
|
+
target: vendor/openspec/validation/parsers/requirement-blocks.ts, vendor/openspec/apply/specs-apply.ts, vendor/openspec/validation/validator.ts
|
|
195
|
+
note: "diffScenarioNames reports both the scenario names a MODIFIED block drops and the ones it introduces, plus both counts, alongside the existing scenario-loss guard; findMissingCurrentScenarios becomes the guard's missing-half read of the same comparison, so archive and validate cannot drift on what they report any more than on what they refuse"
|
|
196
|
+
- upstream: src/core/templates/workflows/{apply-change,archive-change,explore,project-root,propose,sync-specs,update-change}.ts, src/commands/workflow/new-change.ts
|
|
197
|
+
pr: "#1787"
|
|
198
|
+
target: vendor/openspec/generation/templates/workflows/{apply-change,archive-change,explore,project-root,propose,sync-specs,update-change}.ts, src/shim/new-change.ts
|
|
199
|
+
note: "the six emitted workflows carry the shared project-check text (project-root.ts): before the first write, confirm `openspec list --json` reports a root; with none, an auto-selected workflow drops OpenSpec silently while an explicit OpenSpec request stops and asks, and neither branch runs `openspec init` or hand-creates openspec/ as a side effect; `openspec new change` prints a dimmed notice naming the directory when it fell back to an implicit root"
|
|
200
|
+
- upstream: schemas/spec-driven/schema.yaml, src/core/templates/workflows/archive-change.ts, src/utils/task-progress.ts
|
|
201
|
+
pr: "#1773"
|
|
202
|
+
target: canonical/openspec/schema.spec-driven.yaml (override), vendor/openspec/generation/templates/workflows/archive-change.ts, vendor/openspec/engine/task-progress.ts, vendor/openspec/validation/task-lines.ts
|
|
203
|
+
note: "a checkbox is done only when its bracket holds `x`/`X` (any spacing, any case); every other marker, including `- [~]`, `- [-]` and an empty `- []`, counts as open, and a line with no checkbox is not tracked at all"
|
|
204
|
+
- upstream: src/core/templates/workflows/explore.ts
|
|
205
|
+
pr: "#1788"
|
|
206
|
+
target: vendor/openspec/generation/templates/workflows/explore.ts
|
|
207
|
+
note: "explore names the propose workflow at the implement-request handoff, the capture-done handoff, the discovery-end offer, the summary next step, and the don't-implement guardrail; upstream resolves each against the installed workflow set, while explore.ts hardcodes the shipped-workflow wording (`/opsx:propose`, `/opsx:apply`) because FreeHarness always emits both workflows, so no CLI-only fallback is rendered"
|
|
208
|
+
- upstream: src/core/command-generation/generator.ts, src/core/shared/skill-generation.ts, src/core/templates/optional-workflow.ts, src/core/templates/workflows/{apply-change,archive-change,propose,update-change}.ts
|
|
209
|
+
pr: "#1775"
|
|
210
|
+
target: vendor/openspec/generation/templates/workflows/{apply-change,update-change}.ts
|
|
211
|
+
note: "adapted: upstream templates author an `[[opsx:if-workflow <id>]]...[[opsx:else]]...[[opsx:end]]` conditional that generation resolves against the installed workflow set; FreeHarness vendors no resolver and no optional-workflow.ts. The templates are hand-edited with each conditional already resolved for the fixed six-workflow set: continue and new are not emitted, so their handoffs render the CLI fallback, and no emitted text names a workflow FreeHarness does not ship"
|
|
212
|
+
- upstream: src/core/parsers/requirement-blocks.ts, src/core/specs-apply.ts, src/core/validation/validator.ts
|
|
213
|
+
pr: "#1804"
|
|
214
|
+
target: vendor/openspec/validation/parsers/requirement-blocks.ts, vendor/openspec/apply/specs-apply.ts, vendor/openspec/validation/validator.ts
|
|
215
|
+
note: "a well-formed requirement block written outside every delta section is reported as a WARNING naming where it sits, instead of being silently ignored by the section reader"
|
|
216
|
+
- upstream: src/core/templates/workflows/update-change.ts
|
|
217
|
+
pr: "#1840"
|
|
218
|
+
target: vendor/openspec/generation/templates/workflows/update-change.ts
|
|
219
|
+
note: "update-change drafts the requested edit in the conversation during step 4 and performs every artifact write in step 5; no earlier step edits a file, and step 5 shows the drafted edit alongside each proposed revision"
|
|
220
|
+
- upstream: src/utils/task-progress.ts
|
|
221
|
+
pr: "#1862"
|
|
222
|
+
target: vendor/openspec/engine/task-progress.ts, vendor/openspec/validation/task-lines.ts
|
|
223
|
+
note: "the task-line pattern accepts `-`, `*`, `+` and an ordered `1.`/`1)` marker (up to nine digits) ahead of the checkbox, so an unchecked task under a marker other than `-`/`*` is counted instead of read as untracked"
|
|
224
|
+
- upstream: "(FreeHarness, not upstream)"
|
|
225
|
+
pr: "diverges from #1862, #1773"
|
|
226
|
+
target: vendor/openspec/engine/task-progress.ts, vendor/openspec/validation/task-lines.ts, canonical/hooks/freeharness-converge.mjs, canonical/hooks/freeharness-session-start
|
|
227
|
+
note: "the task-line pattern is rewritten in a linear form (`\\[(?:\\s*([^\\]\\s])\\s*\\](?![([])|\\](?![([])|\\s+\\])`): upstream's `\\s*([^\\]\\s]?)\\s*` puts two whitespace runs around an optional token and backtracks quadratically, so one `- [` line of spaces stalls every reader for seconds to minutes. Matches are unchanged except that group 1 is undefined for a blank box, which every done check reads as `(match[1] ?? '').toLowerCase() === 'x'`. The session-start hook also skips lines longer than 10,000 characters before matching"
|
|
228
|
+
- upstream: src/core/templates/workflows/archive-change.ts, src/core/templates/workflows/sync-specs.ts
|
|
229
|
+
pr: "#1701"
|
|
230
|
+
target: vendor/openspec/generation/templates/workflows/{archive-change,sync-specs}.ts
|
|
231
|
+
note: "sync and archive create a new capability's main spec from ADDED requirements only; a delta with a MODIFIED or RENAMED requirement and no existing main spec is reported sync-blocked instead of inventing a prior version, a REMOVED-only delta with no `retire_capabilities` marker is reported blocked (matching archive's 'Spec must have at least one requirement' refusal), and a REMOVED-only delta under that marker is treated as already retired"
|
|
232
|
+
- upstream: src/core/templates/workflows/explore.ts
|
|
233
|
+
pr: "#1832"
|
|
234
|
+
target: vendor/openspec/generation/templates/workflows/explore.ts
|
|
235
|
+
note: "when the user's own request asks explore to capture the exploration as a change, that request is itself the write confirmation the mode otherwise requires, scoped to the change and artifacts the request names; a yes to explore's own offer confirms only the scope that offer named"
|
|
236
|
+
- upstream: src/core/list.ts
|
|
237
|
+
pr: "#1866"
|
|
238
|
+
target: src/shim/list.ts
|
|
239
|
+
note: "a change directory entry that no longer resolves while list dates it (removed mid-listing, a symlink with a missing or looping target) is skipped instead of aborting the whole listing"
|
|
240
|
+
- upstream: src/commands/workflow/instructions.ts, src/core/archive.ts, src/core/validation/validator.ts, src/utils/spec-discovery.ts
|
|
241
|
+
pr: "#1870"
|
|
242
|
+
target: src/shim/instructions.ts, src/shim/archive.ts, vendor/openspec/validation/validator.ts, vendor/openspec/validation/spec-discovery.ts
|
|
243
|
+
note: "a delta file under a change's specs/ that carries delta sections but is not a capability's spec.md (findUnreadDeltaFiles) counts toward archive's has-deltas check, so a change whose only delta content sits at a path the merge never reads is refused instead of archived with nothing merged"
|
|
244
|
+
- upstream: src/core/parsers/markdown-parser.ts, src/core/parsers/requirement-text.ts, src/core/validation/validator.ts
|
|
245
|
+
pr: "#1858"
|
|
246
|
+
target: vendor/openspec/validation/parsers/{markdown-parser,requirement-text}.ts, vendor/openspec/validation/validator.ts
|
|
247
|
+
note: "a scenario header (`#### Scenario: ...`) with no body under it is reported as an error instead of accepted as an empty scenario"
|
|
248
|
+
- upstream: src/core/parsers/change-parser.ts
|
|
249
|
+
pr: "#1856"
|
|
250
|
+
target: vendor/openspec/validation/parsers/change-parser.ts
|
|
251
|
+
note: "the change proposal's What-Changes deltas are read through parseDeltaSpec, the same reader `openspec archive` uses, instead of ChangeParser's own simpler pass, so a proposal and the change it describes cannot disagree on what a delta contains; a REMOVED entry written as a bare name is read as a header-only block"
|
|
252
|
+
- upstream: src/core/parsers/requirement-blocks.ts, src/core/parsers/spec-structure.ts
|
|
253
|
+
pr: "#1860"
|
|
254
|
+
target: vendor/openspec/validation/parsers/requirement-blocks.ts, vendor/openspec/validation/parsers/spec-structure.ts
|
|
255
|
+
note: "normalizeRequirementName drops a trailing run of `#`s preceded by whitespace (an ATX heading's optional closing hashes), so `### Requirement: Foo ###` reads as `Foo`; a name genuinely ending in `#`, like `C#`, is unaffected because the run must be whitespace-preceded; findMainSpecStructureIssues' duplicate-heading check now normalizes through the same function instead of a bare `.trim()`, so a closed heading no longer reads as a distinct duplicate name"
|
|
256
|
+
- upstream: src/core/parsers/requirement-blocks.ts, src/core/specs-apply.ts
|
|
257
|
+
pr: "#1864"
|
|
258
|
+
target: vendor/openspec/validation/parsers/requirement-blocks.ts, vendor/openspec/apply/specs-apply.ts
|
|
259
|
+
note: "foldRequirementName's case- and whitespace-insensitive typo check also covers ADDED headers, not only REMOVED and the RENAMED/REMOVED cross-section conflict, so two ADDED requirement names differing only in case or interior whitespace are refused as a likely typo"
|
|
260
|
+
- upstream: src/core/parsers/requirement-blocks.ts, src/core/specs-apply.ts, src/core/validation/validator.ts
|
|
261
|
+
pr: "#1806"
|
|
262
|
+
target: vendor/openspec/validation/parsers/requirement-blocks.ts, vendor/openspec/apply/specs-apply.ts, vendor/openspec/validation/validator.ts
|
|
263
|
+
note: "a `FROM:`/`TO:` line in `## RENAMED Requirements` that never forms a pair (a second FROM overwriting an unpaired first, a TO with no pending FROM, a trailing FROM at section end) is recorded as an UnpairedRename instead of silently dropped, so validate reports it and buildUpdatedSpec refuses the fold instead of guessing a pairing"
|
|
264
|
+
- upstream: src/core/specs-apply.ts, src/commands/workflow/instructions.ts
|
|
265
|
+
pr: "#1835"
|
|
266
|
+
target: vendor/openspec/apply/specs-apply.ts, src/shim/instructions.ts
|
|
267
|
+
note: "resolveTrustedSpecPath warns when a linked capability spec resolves outside the project root instead of writing there silently; maskHtmlComments becomes a single linear scan instead of a quadratic regex re-scan from every comment opener; printInstructionsText escapes the artifact envelope's tag attributes and inner text (change name, description, rules, dependency description) so a hostile repository's file or directory names cannot forge a sibling tag in the agent-facing instructions output. The commit's dependency-advisory hunks (vitest/fflate version bumps) are excluded: no dependency change is in scope"
|
|
268
|
+
- upstream: src/commands/workflow/status.ts, src/core/change-status-policy.ts
|
|
269
|
+
pr: "#1786"
|
|
270
|
+
target: src/shim/status.ts, vendor/openspec/engine/change-status-policy.ts
|
|
271
|
+
note: "resolveNextStep returns one next step in both a ready-to-run `command` (with any store flag) and a `sentence` form, so the JSON `nextSteps` array and the text surface's printed command name the same next action instead of risking a mismatch between two independent builders"
|
|
272
|
+
- upstream: src/commands/change.ts, src/commands/validate.ts, src/commands/workflow/shared.ts, src/commands/workflow/status.ts, src/core/archive.ts, src/core/list.ts, src/utils/nested-change.ts
|
|
273
|
+
pr: "#1849"
|
|
274
|
+
target: src/shim/{status,list,archive,validate}.ts
|
|
275
|
+
note: "a change laid out under a namespace folder (`changes/<area>/<name>/` instead of directly under `changes/`) is detected from the default schema's root markers (.openspec.yaml, proposal.md, ...) and reported by name and validate, list, status and archive instead of being silently mishandled"
|
|
276
|
+
- upstream: src/core/validation/task-checkboxes.ts, src/core/validation/validator.ts
|
|
277
|
+
pr: "#1774"
|
|
278
|
+
target: src/shim/{shared,status,instructions,archive}.ts
|
|
279
|
+
note: "adapted: when every file the schema tracks as tasks lists items but carries no checkbox (bare `-`/`*`/`+`/ordered bullets with no `[ ]`/`[x]`), findMissingTaskCheckboxWarning returns one warning naming the file and the line of its first bare list item; `status` (JSON warnings and text) and `instructions apply` surface it, and `archive` adds it as an [INFO] blocker, since the archive-blocker gate otherwise reads the zero total as already complete; `validate` does not report it"
|
|
280
|
+
- upstream: src/commands/workflow/instructions.ts, src/commands/workflow/shared.ts
|
|
281
|
+
pr: "#1732"
|
|
282
|
+
target: src/shim/instructions.ts
|
|
283
|
+
note: "generateApplyInstructions resolves every concrete file a glob tracking path matches (resolveArtifactOutputs) instead of one literal path, and reports an unreadable tracking file by path and reason instead of treating it as absent; a change whose tracking evidence is entirely unreadable reports blocked instead of falsely reporting all_done, and unavailableTrackingFiles is carried in the JSON payload alongside taskTrackingConfigured"
|
|
284
|
+
- upstream: src/core/artifact-graph/outputs.ts
|
|
285
|
+
pr: "#1885"
|
|
286
|
+
target: vendor/openspec/engine/outputs.ts
|
|
287
|
+
note: "isGlobPattern recognizes a brace expansion (`{a,b}`, `{1..3}`) and an extglob (`@(...)`, `!(...)`) as a glob in addition to the existing `*`/`?`/`[` check, so an artifact output declared with either is resolved through the glob path instead of treated as one literal filename"
|
|
288
|
+
- upstream: src/core/validation/purpose-placeholder.ts
|
|
289
|
+
pr: "#1912"
|
|
290
|
+
target: vendor/openspec/validation/purpose-placeholder.ts
|
|
291
|
+
note: "a shouted `TBD`/`TODO` opening the Purpose is always the placeholder marker; any other case (`todo`, `Tbd`) is the marker only when punctuation or line end follows it, so lowercase prose that happens to start with those letters in another language (Spanish/Portuguese 'Todo') is not misread as an unwritten Purpose while `todo - write this later` still is"
|
|
292
|
+
- upstream: src/core/specs-apply.ts
|
|
293
|
+
pr: "#1958"
|
|
294
|
+
target: vendor/openspec/apply/specs-apply.ts
|
|
295
|
+
note: "writeUpdatedSpec reads the target spec.md's existing line-ending convention (matchLineEnding/detectLineEnding) before an archive fold rewrites it and writes the rebuilt content back with that same convention instead of always LF, so a spec file already using CRLF keeps every unchanged line unchanged; a spec that does not exist yet stays LF. Scoped to this file: the commit's other hunks (src/utils/file-system.ts's shell-completion marker-block writer, scripts/pack-version-check.mjs) touch no kept path"
|
|
296
|
+
|
|
297
|
+
# vendor-sync-2026-10: kept-path commits between 9d4e5974 and db230978 reviewed and
|
|
298
|
+
# intentionally not ported.
|
|
299
|
+
ports_1_13_2_ignored:
|
|
300
|
+
- pr: "#1962"
|
|
301
|
+
note: "removed-requirement reporting in verify — touches only verify-change.ts; workflow not emitted, text arrives through the adopted asset snapshot"
|
|
302
|
+
- pr: "#1926"
|
|
303
|
+
note: "archive EPERM fallback on Windows rename — no Windows target"
|
|
304
|
+
- pr: "#1769"
|
|
305
|
+
note: "archive lock release on Windows — no Windows target"
|
|
306
|
+
- pr: "#1940"
|
|
307
|
+
note: "approval-threshold wording — touches only ff-change.ts and onboard.ts, neither emitted; workflow not emitted, text arrives through the adopted asset snapshot"
|
|
308
|
+
- pr: "#1777"
|
|
309
|
+
note: "top-level heading in generated artifacts — no markdown lint runs in the gate, the artifacts are read by agents, and the heading adds a line to every artifact of every change; the ported parsers still accept an artifact that has one"
|
|
310
|
+
- pr: "#1852"
|
|
311
|
+
note: "trigger phrases in skill descriptions — the six descriptions are already in the model's skill list on every turn; the slash command is the explicit invocation path"
|
|
312
|
+
- pr: "#1829"
|
|
313
|
+
note: "bulk-archive existing-target check — touches only bulk-archive-change.ts; workflow not emitted, text arrives through the adopted asset snapshot"
|
|
314
|
+
|
|
315
|
+
# vendor-sync-2026-11 ports from upstream db230978 (1.13.2) -> 6d04a1b5 (1.15.0, MIT).
|
|
316
|
+
# One entry per kept-path commit, limited to the hunks that reach emitted workflow text
|
|
317
|
+
# or the store-free shim; `sync adopt sdd` never touches these (tracked assets only).
|
|
318
|
+
ports_1_15_0:
|
|
319
|
+
- commit: "e7a9512d"
|
|
320
|
+
pr: "#1994"
|
|
321
|
+
upstream: src/commands/workflow/instructions.ts, src/commands/workflow/shared.ts, src/core/templates/workflows/apply-change.ts
|
|
322
|
+
target: src/shim/instructions.ts, vendor/openspec/generation/templates/workflows/apply-change.ts
|
|
323
|
+
note: "apply tasks carry their source path and line; our text printer naming path and line is a FreeHarness divergence from upstream's printer"
|
|
324
|
+
- commit: "1bce87f8"
|
|
325
|
+
pr: "#2054"
|
|
326
|
+
upstream: src/core/parsers/requirement-blocks.ts, src/core/specs-apply.ts, src/core/validation/validator.ts
|
|
327
|
+
target: vendor/openspec/validation/parsers/requirement-blocks.ts, vendor/openspec/apply/specs-apply.ts, vendor/openspec/validation/validator.ts, src/shim/archive.ts
|
|
328
|
+
note: "archive warns on delta text it will not carry; the warnings surface through the archive shim"
|
|
329
|
+
- commit: "276d0150"
|
|
330
|
+
pr: "#2104"
|
|
331
|
+
upstream: src/core/templates/workflows/explore.ts, src/core/templates/workflows/propose.ts
|
|
332
|
+
target: vendor/openspec/generation/templates/workflows/{explore,propose}.ts
|
|
333
|
+
note: "glob artifact paths kept inside the change directory; propose and explore halves only, continue-change and ff-change are not emitted"
|
|
334
|
+
- commit: "3fea2a96"
|
|
335
|
+
pr: "#2083"
|
|
336
|
+
upstream: src/core/templates/workflows/update-change.ts
|
|
337
|
+
target: vendor/openspec/generation/templates/workflows/update-change.ts
|
|
338
|
+
note: "glob new-file flow unblocked; update half only, the verify-change half is not emitted"
|
|
339
|
+
- commit: "9cb5ba8c"
|
|
340
|
+
pr: "#2086"
|
|
341
|
+
upstream: src/core/templates/workflows/explore.ts
|
|
342
|
+
target: vendor/openspec/generation/templates/workflows/explore.ts
|
|
343
|
+
note: "explore capture table scoped to the default schema"
|
|
344
|
+
- commit: "0ff63dba"
|
|
345
|
+
pr: "#1759"
|
|
346
|
+
upstream: src/core/templates/workflows/archive-change.ts
|
|
347
|
+
target: vendor/openspec/generation/templates/workflows/archive-change.ts
|
|
348
|
+
note: "archive wording requiring a successful spec sync; archive-change half only, bulk-archive-change is not emitted"
|
|
349
|
+
- commit: "832e2cf8"
|
|
350
|
+
pr: "#2120"
|
|
351
|
+
upstream: src/commands/workflow/new-change.ts
|
|
352
|
+
target: src/shim/new-change.ts
|
|
353
|
+
note: "empty --schema falls back to the default schema; no project config read, since the shim is config-free"
|
|
354
|
+
- commit: "18729828"
|
|
355
|
+
pr: "#1978"
|
|
356
|
+
upstream: src/core/validation/constants.ts
|
|
357
|
+
target: vendor/openspec/validation/constants.ts
|
|
358
|
+
note: "validation constants message only; the schema sentence on the 500-character limit is not adopted (owner decision 2026-10-29)"
|
|
359
|
+
- commit: "7056a580"
|
|
360
|
+
pr: "#1791"
|
|
361
|
+
upstream: src/commands/workflow/instructions.ts, src/core/templates/workflows/apply-change.ts
|
|
362
|
+
target: src/shim/instructions.ts, vendor/openspec/generation/templates/workflows/apply-change.ts
|
|
363
|
+
note: "instruction text only; the schema Workflow follow-up section stays skipped"
|
|
364
|
+
- commit: "e70dcc7c"
|
|
365
|
+
pr: "#1997"
|
|
366
|
+
upstream: schemas/spec-driven/schema.yaml, schemas/spec-driven/templates/proposal.md
|
|
367
|
+
target: canonical/openspec/schema.spec-driven.yaml, canonical/openspec/templates/proposal.md
|
|
368
|
+
note: "capability naming guidance in the proposal instruction and template, wording adapted to the schema override"
|
|
369
|
+
- commit: "852a0738"
|
|
370
|
+
pr: "#2037"
|
|
371
|
+
upstream: schemas/spec-driven/schema.yaml
|
|
372
|
+
target: canonical/openspec/schema.spec-driven.yaml
|
|
373
|
+
note: "spec files written one at a time, wording adapted to the schema override; the continue-change half is not emitted and not ported"
|
|
374
|
+
|
|
375
|
+
# vendor-sync-2026-11: kept-path commits between db230978 and 6d04a1b5 reviewed and
|
|
376
|
+
# intentionally not ported.
|
|
377
|
+
ports_1_15_0_ignored:
|
|
378
|
+
- commit: "760584ba"
|
|
379
|
+
pr: "#2020"
|
|
380
|
+
note: "held: validate --strict would fail on requirements over the length limit, which many of our requirements exceed"
|
|
381
|
+
- commit: "88692b3b"
|
|
382
|
+
pr: "#1925"
|
|
383
|
+
note: "held: warns on unknown .openspec.yaml keys, which our depends_on and supersedes keys would trigger"
|
|
384
|
+
- commit: "e0a2f00f"
|
|
385
|
+
pr: "#2063"
|
|
386
|
+
note: "held: exempts x- extension keys from the unknown-key warning of #1925, which is itself held; it is taken together with #1925 once the warning can coexist with our depends_on and supersedes keys"
|
|
387
|
+
- commit: "6b156199"
|
|
388
|
+
pr: "#2114"
|
|
389
|
+
note: "not emitted: touches only the verify workflow"
|
|
390
|
+
- commit: "43d23ccb"
|
|
391
|
+
pr: "#2028"
|
|
392
|
+
note: "not emitted: touches only the verify workflow"
|
|
393
|
+
- commit: "31c6d1c4"
|
|
394
|
+
pr: "#2137"
|
|
395
|
+
note: "out of slice: standalone binaries for installs without Node.js"
|
|
396
|
+
- commit: "15b3b50f"
|
|
397
|
+
pr: "#2116"
|
|
398
|
+
note: "out of slice: lazy loading of ora in the upstream CLI"
|
|
399
|
+
- commit: "bfa670ed"
|
|
400
|
+
pr: "#2025"
|
|
401
|
+
note: "out of slice: lazy command loading in the upstream CLI"
|
|
402
|
+
- commit: "5251f2ef"
|
|
403
|
+
pr: "#2108"
|
|
404
|
+
note: "out of slice: zero-width TTY spinner, the shim has no spinner"
|
|
405
|
+
- commit: "87a69a2b"
|
|
406
|
+
pr: "#2101"
|
|
407
|
+
note: "symlinked layouts: spec.md symlinks are unsupported upstream per #2135"
|
|
408
|
+
- commit: "90c95c2a"
|
|
409
|
+
pr: "#2052"
|
|
410
|
+
note: "symlinked layouts: symlinked changes and specs directories are unsupported upstream per #2135"
|
|
411
|
+
- commit: "e33245d2"
|
|
412
|
+
pr: "#2103"
|
|
413
|
+
note: "new glob dependency: tinyglobby replaces fast-glob upstream; the shim depends only on yaml and zod and its glob matcher never expands braces; FreeHarness refuses brace-expansion output patterns at schema load instead of expanding them"
|
|
414
|
+
- commit: "7acf2820"
|
|
415
|
+
pr: "#2061"
|
|
416
|
+
note: "new glob dependency: brace nesting limit in the upstream glob expansion, which the shim does not use"
|
|
417
|
+
- commit: "ba0f5087"
|
|
418
|
+
pr: "#1792"
|
|
419
|
+
note: "none: retirement cleanup reporting, the shim archive has no retire path"
|
|
420
|
+
- commit: "cf2859a5"
|
|
421
|
+
pr: "#2014"
|
|
422
|
+
note: "none: store-backed edit roots, the shim is store-free"
|
|
423
|
+
- commit: "77281941"
|
|
424
|
+
pr: "#1977"
|
|
425
|
+
note: "none: the hunk routes the archive sync handoff through optionalWorkflow, which our template drops; sync is always emitted, so the archive skill keeps naming /opsx:sync and the hunk is not taken"
|
|
426
|
+
- commit: "d28fb49c"
|
|
427
|
+
pr: "#1972"
|
|
428
|
+
note: "none: show JSON requirement names, no show command is vendored"
|
|
429
|
+
- commit: "c2304240"
|
|
430
|
+
pr: "#2109"
|
|
431
|
+
note: "out of slice: update --dry-run; FreeHarness update has its own two-pass gate"
|
|
432
|
+
- commit: "c879d13d"
|
|
433
|
+
pr: "#883"
|
|
434
|
+
note: "out of slice: code studio tool adapter; adapters other than claude and opencode are trimmed"
|
|
435
|
+
- commit: "d1642cb5"
|
|
436
|
+
pr: "#1352"
|
|
437
|
+
note: "out of slice: easycode tool adapter; adapters other than claude and opencode are trimmed"
|
|
438
|
+
- commit: "3de7c72c"
|
|
439
|
+
pr: "#1211"
|
|
440
|
+
note: "out of slice: atomcode tool adapter; adapters other than claude and opencode are trimmed"
|
|
441
|
+
- commit: "3c3e6e3d"
|
|
442
|
+
pr: "#1961"
|
|
443
|
+
note: "out of slice: GigaCode tool adapter; adapters other than claude and opencode are trimmed"
|
|
444
|
+
|
|
170
445
|
roadmap:
|
|
446
|
+
- "vendor-sync-2026-10 (sdd) adopt: DONE — `sync adopt sdd` adopted the tracked assets (assets/schema.upstream.yaml, assets/skills) at db230978; baseline bumped here and in sources.yaml to db230978 / 1.13.2; post-adopt `sync check sdd` reports up to date (task 5.2)"
|
|
447
|
+
- "vendor-sync-2026-10 (sdd) triage: DONE — 36 upstream commits 9d4e5974..db230978 on kept paths triaged into ports_1_13_2 (29) and ports_1_13_2_ignored (7) above; #1733 and #1940 resolved against the emitted templates (ported, ignored respectively); #1835 limited to its specs-apply.ts/instructions.ts hunks, dependency-advisory hunks excluded; #1958 limited to its specs-apply.ts hunk, Windows-only/non-kept-path hunks excluded"
|
|
171
448
|
- "vendor-sync-2026-09 (sdd): DONE — 1.13 engine/template ports (ports_1_13 above), tracked assets adopted at 9d4e5974, sources.yaml bumped to 9d4e5974 / 1.13.0"
|
|
172
449
|
- "P3 inc.3: DONE — vendored change-validation slice (parsers + schemas + Validator) + src/validate-change.ts"
|
|
173
450
|
- "P3 inc.4: DONE — store-free `openspec` CLI shim (src/bin/openspec.ts + src/shim/*) over the vendored engine: status/instructions/list/schemas/context/validate/new-change fully working"
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
// FreeHarness auto-memory tier-0 spike (Claude Code SessionEnd hook). OFF BY DEFAULT.
|
|
2
|
-
// When FREEHARNESS_AUTO_MEMORY=on, the session's last project telemetry record
|
|
3
|
-
// (<cwd>/.freeharness/telemetry.jsonl, last valid JSON line) is curated into
|
|
4
|
-
// ~/.claude/freeharness/MEMORY.md as ONE line per project (`- <project>: <change> <task>
|
|
5
|
-
// <tier>: main <N>k, subagents <N>k, observer <N>k|n/a`): a re-run with the same record
|
|
6
|
-
// changes nothing; a newer record replaces the project's line. Nothing else is written;
|
|
7
|
-
// the free-mem plugin stays the memory of record. Never blocks, never prints, exit 0 on every path.
|
|
8
|
-
// File override: FREEHARNESS_AUTO_MEMORY_FILE.
|
|
9
|
-
import fs from "node:fs";
|
|
10
|
-
import os from "node:os";
|
|
11
|
-
import path from "node:path";
|
|
12
|
-
import { fileURLToPath } from "node:url";
|
|
13
|
-
|
|
14
|
-
/** Header of the curated file. */
|
|
15
|
-
export const HEADER = "# FreeHarness auto-memory (tier-0 spike; one line per project from the telemetry log)";
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* True when the spike is switched on.
|
|
19
|
-
* @param {NodeJS.ProcessEnv} env Environment.
|
|
20
|
-
* @returns {boolean} Enabled.
|
|
21
|
-
*/
|
|
22
|
-
export function enabled(env = process.env) {
|
|
23
|
-
return String(env.FREEHARNESS_AUTO_MEMORY || "").trim().toLowerCase() === "on";
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* The curated file path.
|
|
28
|
-
* @returns {string} Absolute path.
|
|
29
|
-
*/
|
|
30
|
-
export function memoryFile() {
|
|
31
|
-
return process.env.FREEHARNESS_AUTO_MEMORY_FILE || path.join(os.homedir(), ".claude", "freeharness", "MEMORY.md");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** `<N>k`, non-finite treated as 0. */
|
|
35
|
-
function kTokens(n) {
|
|
36
|
-
const v = Number.isFinite(n) ? Number(n) : 0;
|
|
37
|
-
return `${Math.round(v / 1000)}k`;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Renders a telemetry tag defensively: caps at 64 characters, keeps only `[\w.\-/ ,()]` (no
|
|
42
|
-
* control characters, no embedded newline that could split the one curated line per project).
|
|
43
|
-
* The producer already sanitizes at write time; this is a second pass against a tampered or
|
|
44
|
-
* pre-fix telemetry line.
|
|
45
|
-
* @param {unknown} s Raw field value.
|
|
46
|
-
* @returns {string} Sanitized value ("" for a non-string).
|
|
47
|
-
*/
|
|
48
|
-
function sanitizeTag(s) {
|
|
49
|
-
return typeof s === "string" ? s.trim().replace(/[^\w.\-/ ,()]/g, "").slice(0, 64).trim() : "";
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* One telemetry record rendered as a curated line: `<change or untagged> <task or -> <tier or
|
|
54
|
-
* ->: main <N>k, subagents <N>k, observer <N>k|n/a`.
|
|
55
|
-
* @param {Record<string, any>} rec Parsed telemetry.jsonl record.
|
|
56
|
-
* @returns {string} Formatted line.
|
|
57
|
-
*/
|
|
58
|
-
function telemetryLine(rec) {
|
|
59
|
-
const change = sanitizeTag(rec.change) || "untagged";
|
|
60
|
-
const task = sanitizeTag(rec.task) || "-";
|
|
61
|
-
const tier = sanitizeTag(rec.tier) || "-";
|
|
62
|
-
const sinks = rec && typeof rec.sinks === "object" && rec.sinks ? rec.sinks : {};
|
|
63
|
-
const main = kTokens(sinks.main && sinks.main.total);
|
|
64
|
-
const subagents = kTokens(sinks.subagents && sinks.subagents.total);
|
|
65
|
-
const observerTokens = sinks.observer && Number.isFinite(sinks.observer.tokens) ? sinks.observer.tokens : null;
|
|
66
|
-
const observer = observerTokens === null ? "n/a" : kTokens(observerTokens);
|
|
67
|
-
return `${change} ${task} ${tier}: main ${main}, subagents ${subagents}, observer ${observer}`;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Last valid record of a project's telemetry file, rendered as a curated line.
|
|
72
|
-
* @param {string} project Project root.
|
|
73
|
-
* @returns {string | null} The line, or null when the file is absent or carries no valid record.
|
|
74
|
-
*/
|
|
75
|
-
export function lastTelemetryLine(project) {
|
|
76
|
-
let text;
|
|
77
|
-
try {
|
|
78
|
-
text = fs.readFileSync(path.join(project, ".freeharness", "telemetry.jsonl"), "utf8");
|
|
79
|
-
} catch {
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
let last = null;
|
|
83
|
-
for (const raw of text.split("\n")) {
|
|
84
|
-
if (!raw.trim()) continue;
|
|
85
|
-
try {
|
|
86
|
-
const rec = JSON.parse(raw);
|
|
87
|
-
if (rec && typeof rec === "object") last = rec;
|
|
88
|
-
} catch {
|
|
89
|
-
// skip malformed lines
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return last ? telemetryLine(last) : null;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Upserts one project line into the curated text (idempotent).
|
|
97
|
-
* @param {string} text Current file text (may be empty).
|
|
98
|
-
* @param {string} project Project name (basename of cwd).
|
|
99
|
-
* @param {string} line Formatted telemetry line.
|
|
100
|
-
* @returns {{text:string, changed:boolean}} New text and whether it differs.
|
|
101
|
-
*/
|
|
102
|
-
export function upsert(text, project, line) {
|
|
103
|
-
const entry = `- ${project}: ${line}`;
|
|
104
|
-
// The file is FreeHarness-owned: header + one `- <project>: ...` line per project.
|
|
105
|
-
const entries = (text || "").split("\n").filter((l) => l.startsWith("- "));
|
|
106
|
-
const idx = entries.findIndex((l) => l.startsWith(`- ${project}: `));
|
|
107
|
-
if (idx >= 0) entries[idx] = entry;
|
|
108
|
-
else entries.push(entry);
|
|
109
|
-
const next = `${HEADER}\n\n${entries.join("\n")}\n`;
|
|
110
|
-
return { text: next, changed: next !== text };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Runs the hook for one payload.
|
|
115
|
-
* @param {Record<string, any>} payload Hook payload.
|
|
116
|
-
* @returns {{changed:boolean, file:string} | null} What happened, or null when nothing applied.
|
|
117
|
-
*/
|
|
118
|
-
export function respond(payload) {
|
|
119
|
-
const cwd = typeof payload.cwd === "string" && payload.cwd ? payload.cwd : process.cwd();
|
|
120
|
-
const line = lastTelemetryLine(cwd);
|
|
121
|
-
if (!line) return null;
|
|
122
|
-
const file = memoryFile();
|
|
123
|
-
let current = "";
|
|
124
|
-
try {
|
|
125
|
-
current = fs.readFileSync(file, "utf8");
|
|
126
|
-
} catch {
|
|
127
|
-
// first write
|
|
128
|
-
}
|
|
129
|
-
const r = upsert(current, path.basename(cwd), line);
|
|
130
|
-
if (r.changed) {
|
|
131
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
132
|
-
fs.writeFileSync(file, r.text, "utf8");
|
|
133
|
-
}
|
|
134
|
-
return { changed: r.changed, file };
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** Entry point: flag-gated, read stdin, curate, stay silent, exit 0. */
|
|
138
|
-
function main() {
|
|
139
|
-
if (!enabled()) return;
|
|
140
|
-
let payload = {};
|
|
141
|
-
try {
|
|
142
|
-
const raw = fs.readFileSync(0, "utf8");
|
|
143
|
-
if (raw.trim()) payload = JSON.parse(raw);
|
|
144
|
-
} catch {
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
if (!payload || typeof payload !== "object") return;
|
|
148
|
-
try {
|
|
149
|
-
respond(payload);
|
|
150
|
-
} catch (e) {
|
|
151
|
-
process.stderr.write(`freeharness-auto-memory: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (process.argv[1] && fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url))) {
|
|
156
|
-
main();
|
|
157
|
-
process.exit(0);
|
|
158
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$comment": "FreeHarness auto-memory tier-0 spike for Claude Code (memory capability, optional; OFF by default). Merged additively into settings.json. When FREEHARNESS_AUTO_MEMORY=on, SessionEnd curates one line per project into ~/.claude/freeharness/MEMORY.md from the last entry of <cwd>/.freeharness/dogfood.md, idempotently. Without the flag the hook is a no-op. Never blocks, never prints, exit 0. timeout 10 raises the 1.5 s SessionEnd budget Claude Code applies by default.",
|
|
3
|
-
"hooks": {
|
|
4
|
-
"SessionEnd": [
|
|
5
|
-
{
|
|
6
|
-
"hooks": [
|
|
7
|
-
{
|
|
8
|
-
"type": "command",
|
|
9
|
-
"command": "node \"${CLAUDE_PROJECT_DIR}/.freeharness/hooks/freeharness-auto-memory.mjs\"",
|
|
10
|
-
"timeout": 10
|
|
11
|
-
}
|
|
12
|
-
]
|
|
13
|
-
}
|
|
14
|
-
]
|
|
15
|
-
}
|
|
16
|
-
}
|