scrumrun 1.5.2 → 2.0.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.
- package/CHANGELOG.md +42 -0
- package/CORE.md +224 -311
- package/DECISIONS.md +95 -11
- package/LICENSE +21 -0
- package/MIGRATION-1-to-2.md +109 -0
- package/README.md +137 -78
- package/SPEC.md +248 -257
- package/bin/claude-install.js +18 -132
- package/bin/scrumrun.js +1259 -174
- package/docs/COMMANDS.md +69 -0
- package/docs/ENTITY-MODEL.md +38 -0
- package/docs/RELEASE.md +44 -0
- package/docs/SCHEMA.md +80 -0
- package/docs/SEMANTIC-MEMORY.md +68 -0
- package/docs/TROUBLESHOOTING.md +66 -0
- package/lib/code-intel/adapter.js +20 -0
- package/lib/code-intel/javascript.js +199 -0
- package/lib/code-intel/learning.js +66 -0
- package/lib/code-intel/scanner.js +126 -0
- package/lib/commands/manifest.js +109 -0
- package/lib/commands/render.js +75 -0
- package/lib/memory/index.js +600 -0
- package/lib/memory/markdown.js +51 -0
- package/lib/memory/service.js +300 -0
- package/lib/runtime/budgets.js +19 -0
- package/lib/runtime/context.js +141 -0
- package/lib/runtime/orchestrator.js +270 -0
- package/lib/runtime/request-engine.js +128 -0
- package/lib/security/secrets.js +23 -0
- package/lib/v2/artifacts.js +343 -0
- package/lib/v2/conformance.js +167 -0
- package/lib/v2/migration.js +1161 -0
- package/lib/v2/project-store.js +44 -0
- package/lib/v2/schema.js +128 -0
- package/package.json +16 -6
- package/scripts/generate-contract-docs.js +113 -0
- package/templates/project/.scrumrun/config.md +4 -7
- package/templates/project/.scrumrun/guardrails.md +23 -0
- package/templates/project/.scrumrun/map.md +4 -16
- package/templates/project/.scrumrun/memory/decisions/.gitkeep +1 -0
- package/templates/project/.scrumrun/memory/dossiers/.gitkeep +1 -0
- package/templates/project/.scrumrun/memory/insights/.gitkeep +1 -0
- package/templates/project/.scrumrun/memory/knowledge/.gitkeep +1 -0
- package/templates/project/.scrumrun/method.json +4 -0
- package/templates/project/.scrumrun/project.md +6 -12
- package/templates/project/.scrumrun/runs/.gitkeep +1 -0
- package/templates/project/.scrumrun/sprints/.gitkeep +1 -0
- package/templates/project/.scrumrun/state.md +21 -0
- package/templates/project/.scrumrun/tasks/.gitkeep +1 -0
- package/templates/project/AGENTS.md +16 -49
- package/templates/project-lean/AGENTS.md +18 -0
- package/templates/shared/skills/scrumrun/SKILL.md +201 -0
- package/templates/codex/prompts/sc-agent.md +0 -14
- package/templates/codex/prompts/sc-backlog.md +0 -13
- package/templates/codex/prompts/sc-challenge.md +0 -57
- package/templates/codex/prompts/sc-config.md +0 -18
- package/templates/codex/prompts/sc-context.md +0 -24
- package/templates/codex/prompts/sc-decisions.md +0 -8
- package/templates/codex/prompts/sc-feature.md +0 -16
- package/templates/codex/prompts/sc-fix.md +0 -21
- package/templates/codex/prompts/sc-goal.md +0 -14
- package/templates/codex/prompts/sc-golden.md +0 -14
- package/templates/codex/prompts/sc-help.md +0 -12
- package/templates/codex/prompts/sc-init.md +0 -14
- package/templates/codex/prompts/sc-intake.md +0 -22
- package/templates/codex/prompts/sc-know.md +0 -75
- package/templates/codex/prompts/sc-map.md +0 -13
- package/templates/codex/prompts/sc-review.md +0 -13
- package/templates/codex/prompts/sc-sprint.md +0 -28
- package/templates/codex/prompts/sc-study.md +0 -23
- package/templates/codex/prompts/sc-uninstall.md +0 -14
- package/templates/codex/prompts/sc-update.md +0 -8
- package/templates/codex/prompts/sc-vault.md +0 -27
- package/templates/codex/skills/scrumrun/SKILL.md +0 -412
- package/templates/opencode/commands/sc-agent.md +0 -14
- package/templates/opencode/commands/sc-backlog.md +0 -13
- package/templates/opencode/commands/sc-challenge.md +0 -57
- package/templates/opencode/commands/sc-config.md +0 -18
- package/templates/opencode/commands/sc-context.md +0 -24
- package/templates/opencode/commands/sc-decisions.md +0 -8
- package/templates/opencode/commands/sc-feature.md +0 -16
- package/templates/opencode/commands/sc-fix.md +0 -21
- package/templates/opencode/commands/sc-goal.md +0 -14
- package/templates/opencode/commands/sc-golden.md +0 -14
- package/templates/opencode/commands/sc-help.md +0 -12
- package/templates/opencode/commands/sc-init.md +0 -14
- package/templates/opencode/commands/sc-intake.md +0 -22
- package/templates/opencode/commands/sc-know.md +0 -75
- package/templates/opencode/commands/sc-map.md +0 -13
- package/templates/opencode/commands/sc-review.md +0 -13
- package/templates/opencode/commands/sc-sprint.md +0 -28
- package/templates/opencode/commands/sc-study.md +0 -23
- package/templates/opencode/commands/sc-uninstall.md +0 -14
- package/templates/opencode/commands/sc-update.md +0 -8
- package/templates/opencode/commands/sc-vault.md +0 -27
- package/templates/opencode/skills/scrumrun/SKILL.md +0 -412
- package/templates/project/.scrumrun/agents.md +0 -36
- package/templates/project/.scrumrun/backlog.md +0 -7
- package/templates/project/.scrumrun/context.md +0 -61
- package/templates/project/.scrumrun/goals/main/decisions.md +0 -9
- package/templates/project/.scrumrun/goals/main/history.md +0 -51
- package/templates/project/.scrumrun/goals/main/sprint.md +0 -54
- package/templates/project/.scrumrun/golden-rules.md +0 -9
- package/templates/project/.scrumrun/knowledge.md +0 -15
- package/templates/project/.scrumrun/runbook.md +0 -101
- package/templates/project/.scrumrun/token-policy.md +0 -43
package/docs/COMMANDS.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# ScrumRun 2.0 Command Reference
|
|
2
|
+
|
|
3
|
+
The canonical grammar is:
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
/sc <noun> <subject> <action> [args]
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Use `/sc` inside a supported AI client. The equivalent CLI form is `npx scrumrun@latest sc ...`. CLI-native workflows execute immediately; reasoning-heavy routes tell the installed agent to execute the validated workflow.
|
|
10
|
+
|
|
11
|
+
## Plan
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
/sc plan intake <request>
|
|
15
|
+
/sc plan intake --approve <token>
|
|
16
|
+
/sc plan task --add|--list|--show|--run|--audit|--cancel|--retry
|
|
17
|
+
/sc plan sprint --add|--list|--show|--start|--complete|--block
|
|
18
|
+
/sc plan feature --add|--list|--show|--activate|--complete
|
|
19
|
+
/sc plan run --list|--show|--validate|--learn|--complete|--resume|--fail|--block
|
|
20
|
+
/sc plan challenge <question>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
CLI-native: intake/approval, Task/Run list/show, Task retry, and Run transitions. A retry requires a failed, blocked, or partial Task and creates a new Run.
|
|
24
|
+
|
|
25
|
+
## Knowledge
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
/sc knowledge fact --add|--list|--show|--approve|--reject|--deprecate|--invalidate
|
|
29
|
+
/sc knowledge decision --add|--list|--show|--resolve|--deprecate|--invalidate
|
|
30
|
+
/sc knowledge insight --propose|--list|--show|--confirm|--stale|--reject|--deprecate|--invalidate
|
|
31
|
+
/sc knowledge dossier --add|--list|--show|--refresh|--stale|--deprecate|--archive
|
|
32
|
+
/sc knowledge context --build|--update|--show|--clear
|
|
33
|
+
/sc knowledge map --build|--show
|
|
34
|
+
/sc knowledge study <focus>
|
|
35
|
+
/sc knowledge vault --add|--list|--show|--remove|--path
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Creation options include `--title`, `--content`, repeated `--evidence`, repeated `--relation "used_by: Target"`, `--subject type:id`, `--source`, `--source-id`, `--confidence`, `--valid-from`, `--valid-until`, and `--review-trigger`. Promotion options accept repeated `--evidence` and `--note`.
|
|
39
|
+
|
|
40
|
+
`study` queries the semantic index. `map --build` regenerates both SQLite and bounded `map.md`. `context --clear` deletes only the disposable index.
|
|
41
|
+
|
|
42
|
+
## Rules and review
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
/sc rules guardrail --add|--list|--show|--retire
|
|
46
|
+
/sc rules reviewer --add|--list|--show|--run
|
|
47
|
+
/sc review code --run
|
|
48
|
+
/sc review artifact --run
|
|
49
|
+
/sc review migration --run
|
|
50
|
+
/sc review release --run
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`review artifact --run` is CLI-native and returns a machine-readable 20-invariant project audit. Other review routes require repository reasoning and remain read-only unless fixes receive separate approval.
|
|
54
|
+
|
|
55
|
+
## Config and lifecycle
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
/sc config project --show|--language|--interaction|--approval|--quick-tasks
|
|
59
|
+
/sc config init --local|--shared|--lean|--no-agent-hint|--force
|
|
60
|
+
/sc config update [all|codex|opencode|claude] [--migrate]
|
|
61
|
+
/sc config migrate --to 2 --dry-run|--apply|--rollback
|
|
62
|
+
/sc config doctor [all|codex|opencode|claude] [--strict]
|
|
63
|
+
/sc config uninstall --force
|
|
64
|
+
/sc config help <topic>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Top-level CLI aliases (`init`, `update`, `migrate`, `doctor`, `uninstall`, `status`) remain available for shell automation. Ordinary update runs only a read-only migration preflight; `--migrate` is explicit application consent.
|
|
68
|
+
|
|
69
|
+
Run `npx scrumrun@latest commands` for grammar rendered directly from the current manifest.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# ScrumRun 2.0 Entity Model
|
|
2
|
+
|
|
3
|
+
This document explains the model. Exact ids, directories, initial states, transitions, structural cardinalities, truth ownership, and authority boundaries are generated from `lib/v2/schema.js` into [`SCHEMA.md`](SCHEMA.md). Do not maintain another hand-written schema table here.
|
|
4
|
+
|
|
5
|
+
## Core distinction
|
|
6
|
+
|
|
7
|
+
| Entity | Question | Lifetime |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Feature | Why does this initiative matter? | Long-lived |
|
|
10
|
+
| Task | What atomic outcome is approved? | Until outcome/closure |
|
|
11
|
+
| Sprint | When are related Tasks grouped? | Timebox/batch |
|
|
12
|
+
| Run | How did one execution attempt happen? | Immutable attempt history |
|
|
13
|
+
| Memory | What do we know, and why? | Reviewed until stale/deprecated/invalidated |
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
FEAT-003
|
|
17
|
+
└── TASK-018
|
|
18
|
+
├── executed_by → RUN-044
|
|
19
|
+
├── included_in → SPRINT-012
|
|
20
|
+
├── constrained_by → DEC-018
|
|
21
|
+
└── generated → INS-041
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Task is always the executable unit. A Task may be independent of a Sprint. Sprint never substitutes for Task. A retry creates another Run for the same Task.
|
|
25
|
+
|
|
26
|
+
## Canonical artifacts
|
|
27
|
+
|
|
28
|
+
All artifacts carry `id`, `kind`, `status`, `created`, `updated`, and `method`. Relations live in frontmatter for structural ownership and in `## Relations` for extensible graph edges. The generated [`SCHEMA.md`](SCHEMA.md) is the authoritative inventory.
|
|
29
|
+
|
|
30
|
+
## Operational flow
|
|
31
|
+
|
|
32
|
+
Approval creates one Task and one Run. The Run progresses `executing → validating → learning → completed|failed|blocked`, while the Task mirrors `running → validating → learning → completed|failed|blocked`. Pair mutations are recoverable.
|
|
33
|
+
|
|
34
|
+
Feature and Sprint provide context/grouping and do not own execution history. Review attaches evidence. Memory explains decisions and constraints across all of them.
|
|
35
|
+
|
|
36
|
+
## Generated projections
|
|
37
|
+
|
|
38
|
+
`state.md` summarizes active work/memory. `map.md` summarizes bounded nodes/edges. SQLite stores the complete derived graph/search index. All are fingerprinted or explicitly stale and can be rebuilt from Markdown plus source code.
|
package/docs/RELEASE.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# ScrumRun 2.0 Release Procedure
|
|
2
|
+
|
|
3
|
+
Publication is owner-gated. Tests, packaging, local commits, and release metadata preparation do not authorize npm publication, git push, tags, GitHub releases, or dist-tag changes.
|
|
4
|
+
|
|
5
|
+
## Local release gate
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm test
|
|
9
|
+
npm run benchmark
|
|
10
|
+
git diff --check
|
|
11
|
+
npm pack --dry-run
|
|
12
|
+
npx scrumrun@latest sc review artifact --run # in a clean v2 fixture/current package equivalent
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Confirm package contents exclude repository-local `.scrumrun/` state, caches, migration records, backups, vaults, tests, and secrets. The `.scrumrun/` directory inside project templates is expected. Confirm `package.json`, README, changelog, SPEC, CORE, skill, migration guide, and tag all name 2.0.0.
|
|
16
|
+
|
|
17
|
+
## Release candidate (owner approval required)
|
|
18
|
+
|
|
19
|
+
1. Set `2.0.0-rc.1` metadata and create a reviewed commit.
|
|
20
|
+
2. `npm pack`; record tarball SHA-256/integrity.
|
|
21
|
+
3. Publish to `next`, never `latest`:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm publish --tag next
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
4. Install from the registry into a clean fixture.
|
|
28
|
+
5. Run init, intake/approval, memory confirmation/query, v1 update preflight/apply/rollback, doctor, and uninstall.
|
|
29
|
+
6. Fix findings in a new RC; never replace an already-published version.
|
|
30
|
+
|
|
31
|
+
## Final promotion (new owner approval required)
|
|
32
|
+
|
|
33
|
+
1. Set `2.0.0`, rerun every local/registry gate, and create the final metadata commit.
|
|
34
|
+
2. Create/push tag `v2.0.0` and publish npm `2.0.0`.
|
|
35
|
+
3. Promote `latest` only after registry smoke tests pass.
|
|
36
|
+
4. Create the GitHub release from `CHANGELOG.md` and link the migration guide.
|
|
37
|
+
5. Verify npm metadata, Git tag/commit, GitHub release, README, and checksums agree.
|
|
38
|
+
|
|
39
|
+
## Recovery
|
|
40
|
+
|
|
41
|
+
- RC defect: publish another RC; keep `latest` unchanged.
|
|
42
|
+
- Final package defect before `latest`: do not promote; publish `2.0.1` after correction if 2.0.0 already exists.
|
|
43
|
+
- Defect after `latest`: assess deprecation vs immediate 2.0.1; never overwrite/unpublish without explicit owner decision and npm-policy review.
|
|
44
|
+
- Migration issue: stop promotion, preserve registry artifact/checksum, use the documented rollback fixture, and publish a corrected version.
|
package/docs/SCHEMA.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# ScrumRun 2.0.0 Executable Schema
|
|
2
|
+
|
|
3
|
+
> Generated by `scripts/generate-contract-docs.js` from `lib/v2/schema.js`. Do not edit this file manually.
|
|
4
|
+
|
|
5
|
+
## Authority boundaries
|
|
6
|
+
|
|
7
|
+
| Concern | Authoritative source | Scope |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| semantics | `SPEC.md` | normative meanings and invariants |
|
|
10
|
+
| schema | `lib/v2/schema.js` | machine-enforced ids, paths, statuses, transitions, relations, and ownership metadata |
|
|
11
|
+
| commands | `lib/commands/manifest.js` | public command grammar and compatibility routes |
|
|
12
|
+
| projectPolicy | `.scrumrun/guardrails.md` | owner/project constraints |
|
|
13
|
+
| projectTruth | `.scrumrun/**/*.md` | authored canonical project records |
|
|
14
|
+
| projections | `.scrumrun/state.md, .scrumrun/map.md, .scrumrun/.cache/` | disposable derived views; never authority |
|
|
15
|
+
|
|
16
|
+
The boundaries above are deliberately different: SPEC owns meanings, the executable schema owns mechanically enforced values, the command manifest owns grammar, project Markdown owns authored project truth, and generated views own nothing.
|
|
17
|
+
|
|
18
|
+
## Artifact contract
|
|
19
|
+
|
|
20
|
+
| Kind | Stable ID | Canonical directory | Allowed initial status | Truth owned by this artifact |
|
|
21
|
+
|---|---|---|---|---|
|
|
22
|
+
| feature | `FEAT-NNN` | `features/` | `backlog`, `proposed` | initiative purpose, scope, dependencies, and lifecycle |
|
|
23
|
+
| task | `TASK-NNN` | `tasks/` | `backlog`, `proposed`, `running` | scope, acceptance criteria, approval, and intended status |
|
|
24
|
+
| sprint | `SPRINT-NNN` | `sprints/` | `proposed` | timebox or delivery-batch membership; never execution history |
|
|
25
|
+
| run | `RUN-NNN` | `runs/` | `executing` | append-only execution events, evidence, result, and attempt number |
|
|
26
|
+
| review | `REV-NNN` | `reviews/` | `proposed` | scoped findings, checks, evidence, and verdict |
|
|
27
|
+
| knowledge | `K-NNN` | `memory/knowledge/` | `candidate` | approved evidence-backed fact and validity |
|
|
28
|
+
| decision | `DEC-NNN` | `memory/decisions/` | `open` | decision, rationale, validity, and lifecycle |
|
|
29
|
+
| insight | `INS-NNN` | `memory/insights/` | `candidate` | explanatory candidate/confirmed context and evidence |
|
|
30
|
+
| dossier | `DOS-NNN` | `memory/dossiers/` | `active` | reviewed topic bundle and freshness |
|
|
31
|
+
|
|
32
|
+
Every artifact also requires `id`, `kind`, `status`, `created`, `updated`, and `method: 2.0.0`.
|
|
33
|
+
|
|
34
|
+
## Structural cardinalities
|
|
35
|
+
|
|
36
|
+
| Frontmatter field | Target | Cardinality | Meaning |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| `feature` | feature (`FEAT-NNN`) | 0..1 | long-lived initiative containing the artifact |
|
|
39
|
+
| `sprint` | sprint (`SPRINT-NNN`) | 0..1 | optional delivery batch containing a Task or Run |
|
|
40
|
+
| `task` | task (`TASK-NNN`) | 1 for Run; otherwise 0..1 | atomic work executed or reviewed by the artifact |
|
|
41
|
+
|
|
42
|
+
Task is the atomic unit. A Task may have zero or one Sprint. A Task may have many Runs, but every Run belongs to exactly one Task and has a monotonically increasing attempt number within that Task. Sprint membership is authoritative on `Task.sprint`; a Sprint's `## Tasks` list is a human-readable projection that must agree with it.
|
|
43
|
+
|
|
44
|
+
## Scalar constraints
|
|
45
|
+
|
|
46
|
+
| Field | Kinds | Presence | Type | Meaning |
|
|
47
|
+
|---|---|---|---|---|
|
|
48
|
+
| `attempt` | run | required | positive integer | monotonic execution-attempt number within one Task |
|
|
49
|
+
|
|
50
|
+
Native creation uses the declared initial statuses. Migration may restore a historical non-initial status only with provenance and validation.
|
|
51
|
+
|
|
52
|
+
## Truth questions
|
|
53
|
+
|
|
54
|
+
- **feature:** Why does this initiative exist?
|
|
55
|
+
- **task:** What approved atomic outcome is intended?
|
|
56
|
+
- **sprint:** When are related Tasks grouped?
|
|
57
|
+
- **run:** How did one execution attempt actually happen?
|
|
58
|
+
- **review:** What independent validation was performed?
|
|
59
|
+
- **knowledge:** What verified project fact is reusable?
|
|
60
|
+
- **decision:** What normative choice constrains future work?
|
|
61
|
+
- **insight:** Why is something arranged or constrained this way?
|
|
62
|
+
- **dossier:** What evidence belongs to one retrieval topic?
|
|
63
|
+
|
|
64
|
+
## Lifecycles
|
|
65
|
+
|
|
66
|
+
| Kind | Declared transitions |
|
|
67
|
+
|---|---|
|
|
68
|
+
| feature | `backlog` → `proposed`, `active`, `cancelled`<br>`proposed` → `active`, `cancelled`<br>`active` → `paused`, `completed`, `cancelled`<br>`completed` → terminal<br>`paused` → `active`, `cancelled`<br>`cancelled` → terminal |
|
|
69
|
+
| task | `backlog` → `proposed`, `running`, `cancelled`<br>`proposed` → `running`, `cancelled`<br>`running` → `validating`, `failed`, `blocked`, `cancelled`<br>`validating` → `learning`, `failed`, `blocked`<br>`learning` → `completed`, `failed`, `blocked`<br>`partial` → `running`, `cancelled`<br>`completed` → terminal<br>`failed` → `running`, `cancelled`<br>`blocked` → `running`, `cancelled`<br>`cancelled` → terminal |
|
|
70
|
+
| sprint | `proposed` → `running`, `cancelled`<br>`running` → `partial`, `completed`, `blocked`, `cancelled`<br>`partial` → `running`, `completed`, `cancelled`<br>`completed` → terminal<br>`blocked` → `running`, `cancelled`<br>`cancelled` → terminal |
|
|
71
|
+
| run | `executing` → `validating`, `failed`, `blocked`<br>`validating` → `learning`, `failed`, `blocked`<br>`learning` → `completed`, `failed`, `blocked`<br>`partial` → `executing`, `failed`, `blocked`<br>`completed` → terminal<br>`failed` → terminal<br>`blocked` → `executing`, `failed` |
|
|
72
|
+
| review | `proposed` → `running`<br>`running` → `passed`, `failed`<br>`passed` → `archived`<br>`failed` → `running`, `archived`<br>`archived` → terminal |
|
|
73
|
+
| knowledge | `candidate` → `approved`, `rejected`<br>`approved` → `deprecated`, `invalidated`<br>`rejected` → `candidate`<br>`deprecated` → `approved`<br>`invalidated` → terminal |
|
|
74
|
+
| decision | `open` → `resolved`, `deprecated`, `invalidated`<br>`resolved` → `deprecated`, `invalidated`<br>`deprecated` → `open`<br>`invalidated` → terminal |
|
|
75
|
+
| insight | `candidate` → `confirmed`, `invalidated`<br>`confirmed` → `stale`, `deprecated`, `invalidated`<br>`stale` → `confirmed`, `deprecated`, `invalidated`<br>`deprecated` → `confirmed`<br>`invalidated` → terminal |
|
|
76
|
+
| dossier | `active` → `stale`, `deprecated`, `archived`<br>`stale` → `active`, `deprecated`, `archived`<br>`deprecated` → `archived`<br>`archived` → terminal |
|
|
77
|
+
|
|
78
|
+
## Projections
|
|
79
|
+
|
|
80
|
+
`state.md`, `map.md`, context packages, and `.cache/` are disposable. They may summarize or index canonical artifacts, but they cannot introduce status, policy, relations, decisions, or knowledge.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Semantic Memory
|
|
2
|
+
|
|
3
|
+
ScrumRun memory explains why code exists in its current form. It separates operational events, normative Decisions, reviewed facts, and contextual Insights so an agent can retrieve the smallest relevant evidence package.
|
|
4
|
+
|
|
5
|
+
## Choosing a kind
|
|
6
|
+
|
|
7
|
+
- Knowledge: descriptive fact verified by evidence.
|
|
8
|
+
- Decision: normative constraint—what must or must not happen.
|
|
9
|
+
- Insight: explanation, rationale, trade-off, warning, failure history, or testing note.
|
|
10
|
+
- Dossier: curated evidence bundle for a topic/module.
|
|
11
|
+
|
|
12
|
+
“Pricing calculations must run on the backend” is a Decision. “This function stays in pricing because quotations use it before checkout exists” is an Insight.
|
|
13
|
+
|
|
14
|
+
## Evidence and lifecycle
|
|
15
|
+
|
|
16
|
+
AI-created facts/insights always start as candidates. Confirmation requires one or more resolvable artifact IDs or project paths. Paths cannot traverse outside the project, follow symlinks, or reference the vault.
|
|
17
|
+
|
|
18
|
+
Useful metadata:
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
subject_type: symbol
|
|
22
|
+
subject_id: function:calculateFinalPrice
|
|
23
|
+
source_type: run
|
|
24
|
+
source_id: RUN-044
|
|
25
|
+
confidence: 0.8
|
|
26
|
+
valid_from: 2026-01-01
|
|
27
|
+
valid_until: null
|
|
28
|
+
last_verified_commit: <git-hash-or-unavailable>
|
|
29
|
+
review_trigger: pricing implementation changes
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Confirmed memory may become stale, deprecated, or invalidated. Those records remain auditable. Queries exclude rejected/deprecated/invalidated truth by default and label stale evidence explicitly.
|
|
33
|
+
|
|
34
|
+
## Relations and code graph
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
symbol:calculateFinalPrice
|
|
38
|
+
├── defined_in → module:pricing
|
|
39
|
+
├── depends_on → TaxCalculator
|
|
40
|
+
├── used_by → OrderService
|
|
41
|
+
├── constrained_by → DEC-018
|
|
42
|
+
├── has_insight → INS-041
|
|
43
|
+
└── protected_by → price-calculation.spec.ts
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The JS/TS adapter records qualified symbol identity, path, kind, line, fingerprint, commit, exports, and lexical/module provenance. Moves with identical fingerprints remap. Unprovable rename/removal becomes orphaned history. Evidence or implementation drift marks linked records stale only in the projection; canonical Markdown changes only through explicit review.
|
|
47
|
+
|
|
48
|
+
## Learning from Runs
|
|
49
|
+
|
|
50
|
+
A validated Run may contain:
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
## Learning Candidates
|
|
54
|
+
|
|
55
|
+
- [placement_rationale] function:calculateFinalPrice | Keep pricing in the domain because quotations use it. | evidence: src/quotation/service.ts | relations: used_by=module:src/quotation/service.ts
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Entering `learning` extracts `INS-NNN` candidates. Malformed extraction emits a warning and never blocks the Run.
|
|
59
|
+
|
|
60
|
+
## Query and maintenance
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npx scrumrun@latest sc knowledge map --build
|
|
64
|
+
npx scrumrun@latest sc knowledge study calculateFinalPrice
|
|
65
|
+
npx scrumrun@latest sc knowledge context --clear
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
SQLite is ignored and disposable. Queries default to 10 records/40 relations and hard-cap at 100/100. Match type, truth state, warnings, relation counts, and evidence are returned so recommendations remain explainable.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
## “Project must be explicitly migrated to v2”
|
|
4
|
+
|
|
5
|
+
Run `npx scrumrun@latest update` for a read-only preflight, then review and apply with `update --migrate`. See [`MIGRATION-1-to-2.md`](../MIGRATION-1-to-2.md).
|
|
6
|
+
|
|
7
|
+
## Migration is blocked
|
|
8
|
+
|
|
9
|
+
- Symlink: replace it with a regular local file/directory or preserve it outside `.scrumrun/`.
|
|
10
|
+
- Malformed `method.json`: do not guess; restore the correct v1 state/marker and rerun dry-run.
|
|
11
|
+
- Secret outside vault: move the value to `.scrumrun/vault.local.md`, redact the canonical source, and retry.
|
|
12
|
+
- Ambiguous history: warnings are expected and preserved; blockers must be resolved before apply.
|
|
13
|
+
|
|
14
|
+
Dry-run is safe to repeat and writes nothing.
|
|
15
|
+
|
|
16
|
+
## Rollback refuses
|
|
17
|
+
|
|
18
|
+
Rollback detected post-migration work that it would erase. Copy/export wanted changes, reconcile the paths named in the error, and retry. Never delete the migration backup to force rollback.
|
|
19
|
+
|
|
20
|
+
## `state.md` or `map.md` is stale
|
|
21
|
+
|
|
22
|
+
`state.md` refreshes after runtime transitions. Rebuild the semantic map with:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx scrumrun@latest sc knowledge map --build
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Stale generated views are warnings, not canonical corruption.
|
|
29
|
+
|
|
30
|
+
## SQLite is missing/corrupt
|
|
31
|
+
|
|
32
|
+
Delete or clear only the disposable cache:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx scrumrun@latest sc knowledge context --clear
|
|
36
|
+
npx scrumrun@latest sc knowledge map --build
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Canonical Markdown is unchanged. Query automatically rebuilds a missing/stale index.
|
|
40
|
+
|
|
41
|
+
## Memory cannot be confirmed
|
|
42
|
+
|
|
43
|
+
Every confirmed fact/insight/Decision needs resolvable evidence. Use an existing artifact ID such as `RUN-044`/`DEC-018` or a regular project path. Absolute paths, traversal, symlinks, missing files, and vault references are rejected.
|
|
44
|
+
|
|
45
|
+
## A query returns stale/orphaned memory
|
|
46
|
+
|
|
47
|
+
Inspect its invalidation warning and code/evidence relations. For a real code move, rebuild may show `remappedFrom`. For a rename/removal without proof, the old symbol remains orphaned. Review the canonical memory; mark stale and reconfirm with current evidence, or invalidate it.
|
|
48
|
+
|
|
49
|
+
## Doctor reports missing clients
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx scrumrun@latest install codex
|
|
53
|
+
npx scrumrun@latest install claude
|
|
54
|
+
npx scrumrun@latest install opencode
|
|
55
|
+
npx scrumrun@latest doctor all
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Use `doctor --compat` only while validating one-cycle v1 adapters.
|
|
59
|
+
|
|
60
|
+
## Node.js is unsupported
|
|
61
|
+
|
|
62
|
+
ScrumRun 2.0 requires Node.js `>=22.13.0` because semantic indexing uses native `node:sqlite`. Upgrade Node, then rerun doctor.
|
|
63
|
+
|
|
64
|
+
## Safe uninstall
|
|
65
|
+
|
|
66
|
+
`uninstall` previews. `uninstall --force` removes `.scrumrun/` and only removes `AGENTS.md` when it is recognized as ScrumRun-generated. Export any memory you want to keep first.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
class LanguageAdapter {
|
|
4
|
+
constructor({ id, extensions }) {
|
|
5
|
+
if (!id || !Array.isArray(extensions) || !extensions.length) throw new Error("LanguageAdapter requires id and extensions.");
|
|
6
|
+
this.id = id;
|
|
7
|
+
this.extensions = new Set(extensions.map((extension) => extension.toLowerCase()));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
supports(file) {
|
|
11
|
+
const extension = file.slice(file.lastIndexOf(".")).toLowerCase();
|
|
12
|
+
return this.extensions.has(extension);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
scan() {
|
|
16
|
+
throw new Error(`${this.id} adapter must implement scan().`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = { LanguageAdapter };
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
const { LanguageAdapter } = require("./adapter");
|
|
6
|
+
const { sha256 } = require("../v2/artifacts");
|
|
7
|
+
|
|
8
|
+
function posix(value) {
|
|
9
|
+
return value.split(path.sep).join("/");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function maskSource(source) {
|
|
13
|
+
const chars = [...source];
|
|
14
|
+
let state = "code";
|
|
15
|
+
let escaped = false;
|
|
16
|
+
for (let index = 0; index < chars.length; index++) {
|
|
17
|
+
const char = chars[index];
|
|
18
|
+
const next = chars[index + 1];
|
|
19
|
+
if (state === "line") {
|
|
20
|
+
if (char === "\n") state = "code";
|
|
21
|
+
else chars[index] = " ";
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (state === "block") {
|
|
25
|
+
if (char === "*" && next === "/") {
|
|
26
|
+
chars[index] = chars[index + 1] = " ";
|
|
27
|
+
index++;
|
|
28
|
+
state = "code";
|
|
29
|
+
} else if (char !== "\n") chars[index] = " ";
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (["single", "double", "template"].includes(state)) {
|
|
33
|
+
if (char !== "\n") chars[index] = " ";
|
|
34
|
+
if (escaped) {
|
|
35
|
+
escaped = false;
|
|
36
|
+
} else if (char === "\\") {
|
|
37
|
+
escaped = true;
|
|
38
|
+
} else if ((state === "single" && char === "'") || (state === "double" && char === '"') || (state === "template" && char === "`")) {
|
|
39
|
+
state = "code";
|
|
40
|
+
}
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (char === "/" && next === "/") {
|
|
44
|
+
chars[index] = chars[index + 1] = " ";
|
|
45
|
+
index++;
|
|
46
|
+
state = "line";
|
|
47
|
+
} else if (char === "/" && next === "*") {
|
|
48
|
+
chars[index] = chars[index + 1] = " ";
|
|
49
|
+
index++;
|
|
50
|
+
state = "block";
|
|
51
|
+
} else if (char === "'") {
|
|
52
|
+
chars[index] = " ";
|
|
53
|
+
state = "single";
|
|
54
|
+
} else if (char === '"') {
|
|
55
|
+
chars[index] = " ";
|
|
56
|
+
state = "double";
|
|
57
|
+
} else if (char === "`") {
|
|
58
|
+
chars[index] = " ";
|
|
59
|
+
state = "template";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return chars.join("");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function closingBrace(masked, open) {
|
|
66
|
+
if (open < 0 || masked[open] !== "{") return -1;
|
|
67
|
+
let depth = 0;
|
|
68
|
+
for (let index = open; index < masked.length; index++) {
|
|
69
|
+
if (masked[index] === "{") depth++;
|
|
70
|
+
else if (masked[index] === "}" && --depth === 0) return index + 1;
|
|
71
|
+
}
|
|
72
|
+
return -1;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function declarationEnd(masked, start) {
|
|
76
|
+
const open = masked.indexOf("{", start);
|
|
77
|
+
const semicolon = masked.indexOf(";", start);
|
|
78
|
+
if (open >= 0 && (semicolon < 0 || open < semicolon)) {
|
|
79
|
+
const close = closingBrace(masked, open);
|
|
80
|
+
if (close >= 0) return close;
|
|
81
|
+
}
|
|
82
|
+
const newline = masked.indexOf("\n", start);
|
|
83
|
+
return semicolon >= 0 ? semicolon + 1 : newline >= 0 ? newline : masked.length;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function lineAt(source, index) {
|
|
87
|
+
return source.slice(0, index).split("\n").length;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function declarations(source) {
|
|
91
|
+
const masked = maskSource(source);
|
|
92
|
+
const patterns = [
|
|
93
|
+
{ kind: "function", regex: /\b(?:export\s+(?:default\s+)?)?(?:async\s+)?function\s*\*?\s*([A-Za-z_$][\w$]*)\s*\(/g },
|
|
94
|
+
{ kind: "class", regex: /\b(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)\b/g },
|
|
95
|
+
{ kind: "interface", regex: /\b(?:export\s+)?interface\s+([A-Za-z_$][\w$]*)\b/g },
|
|
96
|
+
{ kind: "type", regex: /\b(?:export\s+)?type\s+([A-Za-z_$][\w$]*)\s*(?:<[^;=]+>)?\s*=/g },
|
|
97
|
+
{ kind: "enum", regex: /\b(?:export\s+)?(?:const\s+)?enum\s+([A-Za-z_$][\w$]*)\b/g },
|
|
98
|
+
{ kind: "function", regex: /\b(?:export\s+(?:default\s+)?)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::[^=;]+)?=\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/g }
|
|
99
|
+
];
|
|
100
|
+
const found = [];
|
|
101
|
+
const seen = new Set();
|
|
102
|
+
for (const pattern of patterns) {
|
|
103
|
+
pattern.regex.lastIndex = 0;
|
|
104
|
+
let match;
|
|
105
|
+
while ((match = pattern.regex.exec(masked))) {
|
|
106
|
+
const key = `${match.index}:${match[1]}`;
|
|
107
|
+
if (seen.has(key)) continue;
|
|
108
|
+
seen.add(key);
|
|
109
|
+
const end = declarationEnd(masked, match.index);
|
|
110
|
+
const normalized = masked.slice(match.index, end).replace(/\s+/g, " ").trim();
|
|
111
|
+
found.push({
|
|
112
|
+
name: match[1],
|
|
113
|
+
kind: pattern.kind,
|
|
114
|
+
line: lineAt(source, match.index),
|
|
115
|
+
start: match.index,
|
|
116
|
+
end,
|
|
117
|
+
exported: /^\s*export\b/.test(masked.slice(Math.max(0, match.index - 20), match.index + 20)) || /\bexport\b/.test(match[0]),
|
|
118
|
+
fingerprint: sha256(`${pattern.kind}\0${normalized}`)
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { masked, declarations: found.sort((a, b) => a.start - b.start) };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function importSpecifiers(source) {
|
|
126
|
+
const specifiers = new Set();
|
|
127
|
+
const patterns = [
|
|
128
|
+
/^\s*(?:import|export)\s+(?:[^"'\n]*?\s+from\s+)?["']([^"']+)["']/gm,
|
|
129
|
+
/\brequire\s*\(\s*["']([^"']+)["']\s*\)/g,
|
|
130
|
+
/\bimport\s*\(\s*["']([^"']+)["']\s*\)/g
|
|
131
|
+
];
|
|
132
|
+
for (const regex of patterns) {
|
|
133
|
+
let match;
|
|
134
|
+
while ((match = regex.exec(source))) specifiers.add(match[1]);
|
|
135
|
+
}
|
|
136
|
+
return [...specifiers].sort();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function resolveImport(projectRoot, sourceFile, specifier) {
|
|
140
|
+
if (!specifier.startsWith(".")) return `package:${specifier}`;
|
|
141
|
+
const base = path.resolve(path.dirname(sourceFile), specifier);
|
|
142
|
+
const candidates = [
|
|
143
|
+
base,
|
|
144
|
+
...[".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"].map((extension) => `${base}${extension}`),
|
|
145
|
+
...[".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"].map((extension) => path.join(base, `index${extension}`))
|
|
146
|
+
];
|
|
147
|
+
const target = candidates.find((candidate) => fs.existsSync(candidate) && fs.statSync(candidate).isFile());
|
|
148
|
+
if (!target) return `module:${posix(path.relative(projectRoot, base))}`;
|
|
149
|
+
return `module:${posix(path.relative(projectRoot, target))}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
class JavaScriptAdapter extends LanguageAdapter {
|
|
153
|
+
constructor() {
|
|
154
|
+
super({ id: "javascript-typescript", extensions: [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"] });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
scan({ projectRoot, file, relative, commit }) {
|
|
158
|
+
const source = fs.readFileSync(file, "utf8");
|
|
159
|
+
const parsed = declarations(source);
|
|
160
|
+
const moduleId = `module:${posix(relative)}`;
|
|
161
|
+
const dependencies = importSpecifiers(source).map((specifier) => ({
|
|
162
|
+
specifier,
|
|
163
|
+
target: resolveImport(projectRoot, file, specifier)
|
|
164
|
+
}));
|
|
165
|
+
const symbols = parsed.declarations.map((declaration) => ({
|
|
166
|
+
id: `symbol:${posix(relative)}#${declaration.kind}:${declaration.name}`,
|
|
167
|
+
name: declaration.name,
|
|
168
|
+
qualifiedName: `${posix(relative)}#${declaration.kind}:${declaration.name}`,
|
|
169
|
+
kind: declaration.kind,
|
|
170
|
+
path: posix(relative),
|
|
171
|
+
line: declaration.line,
|
|
172
|
+
fingerprint: declaration.fingerprint,
|
|
173
|
+
commit,
|
|
174
|
+
exported: declaration.exported,
|
|
175
|
+
language: this.id,
|
|
176
|
+
start: declaration.start,
|
|
177
|
+
end: declaration.end
|
|
178
|
+
}));
|
|
179
|
+
return {
|
|
180
|
+
module: {
|
|
181
|
+
id: moduleId,
|
|
182
|
+
name: posix(relative),
|
|
183
|
+
qualifiedName: posix(relative),
|
|
184
|
+
kind: "module",
|
|
185
|
+
path: posix(relative),
|
|
186
|
+
line: 1,
|
|
187
|
+
fingerprint: sha256(source),
|
|
188
|
+
commit,
|
|
189
|
+
exported: true,
|
|
190
|
+
language: this.id
|
|
191
|
+
},
|
|
192
|
+
symbols,
|
|
193
|
+
dependencies,
|
|
194
|
+
masked: parsed.masked
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
module.exports = { JavaScriptAdapter, declarations, importSpecifiers, maskSource, resolveImport };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
const { ArtifactRepository, sha256 } = require("../v2/artifacts");
|
|
5
|
+
const { createMemory } = require("../memory/service");
|
|
6
|
+
|
|
7
|
+
function learningSection(body) {
|
|
8
|
+
const match = String(body || "").match(/^## Learning Candidates\s*\r?\n([\s\S]*?)(?=^## |(?![\s\S]))/m);
|
|
9
|
+
return match ? match[1] : "";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function parseCandidate(line) {
|
|
13
|
+
const match = line.match(/^\s*-\s*\[([a-z][a-z0-9_]*)\]\s+([^|]+?)\s*\|\s*([^|]+?)(?:\s*\|\s*evidence:\s*([^|]+?))?(?:\s*\|\s*relations?:\s*(.+?))?\s*$/i);
|
|
14
|
+
if (!match) return null;
|
|
15
|
+
const subject = match[2].trim();
|
|
16
|
+
const evidence = match[4] ? match[4].split(/\s*,\s*/).filter(Boolean) : [];
|
|
17
|
+
const relations = match[5]
|
|
18
|
+
? match[5].split(/\s*,\s*/).map((item) => item.replace(/^([a-z][a-z0-9_]*)\s*=\s*/i, "$1: ")).filter(Boolean)
|
|
19
|
+
: [];
|
|
20
|
+
return { insightKind: match[1].toLowerCase(), subject, content: match[3].trim(), evidence, relations };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function extractLearningCandidates(projectRoot, runId) {
|
|
24
|
+
const repository = new ArtifactRepository(path.join(projectRoot, ".scrumrun"));
|
|
25
|
+
const run = repository.read("run", runId);
|
|
26
|
+
if (!run) throw new Error(`Run not found: ${runId}`);
|
|
27
|
+
if (!run.record || !["learning", "completed"].includes(run.record.status)) {
|
|
28
|
+
throw new Error(`Run ${runId} must be learning or completed before extracting knowledge.`);
|
|
29
|
+
}
|
|
30
|
+
const lines = learningSection(run.body).split(/\r?\n/).filter((line) => /^\s*-\s+/.test(line) && !/Pending\./i.test(line));
|
|
31
|
+
const created = [];
|
|
32
|
+
const warnings = [];
|
|
33
|
+
const seen = new Set();
|
|
34
|
+
for (const line of lines) {
|
|
35
|
+
const candidate = parseCandidate(line);
|
|
36
|
+
if (!candidate) {
|
|
37
|
+
warnings.push(`Ignored malformed learning candidate: ${line.trim()}`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const key = sha256(`${candidate.subject}\0${candidate.content}`);
|
|
41
|
+
if (seen.has(key)) continue;
|
|
42
|
+
seen.add(key);
|
|
43
|
+
const generic = candidate.subject.match(/^(function|class|interface|type|enum):(.+)$/);
|
|
44
|
+
const subjectType = candidate.subject.startsWith("symbol:") || generic ? "symbol" : "module";
|
|
45
|
+
try {
|
|
46
|
+
const artifact = createMemory(projectRoot, "insight", {
|
|
47
|
+
title: candidate.content.length > 100 ? `${candidate.content.slice(0, 97)}...` : candidate.content,
|
|
48
|
+
content: candidate.content,
|
|
49
|
+
evidence: [runId, ...candidate.evidence],
|
|
50
|
+
relations: [`derived_from: ${runId}`, `insight_kind: ${candidate.insightKind}`, ...candidate.relations],
|
|
51
|
+
subjectType,
|
|
52
|
+
subjectId: candidate.subject,
|
|
53
|
+
sourceType: "run",
|
|
54
|
+
sourceId: runId,
|
|
55
|
+
confidence: 0.6,
|
|
56
|
+
reviewTrigger: "subject implementation or evidence changes"
|
|
57
|
+
});
|
|
58
|
+
created.push(artifact.record.id);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
warnings.push(`Could not create candidate for ${candidate.subject}: ${error.message}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { created, warnings };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = { extractLearningCandidates, learningSection, parseCandidate };
|