@jaimevalasek/aioson 1.9.3 → 1.16.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 +188 -0
- package/README.md +44 -1
- package/package.json +1 -1
- package/src/cli.js +45 -1
- package/src/commands/op-capture.js +146 -0
- package/src/commands/op-forget.js +54 -0
- package/src/commands/op-identity.js +145 -0
- package/src/commands/op-list.js +105 -0
- package/src/commands/op-migrate.js +158 -0
- package/src/commands/op-promote.js +66 -0
- package/src/commands/op-reinforce.js +73 -0
- package/src/commands/op-show.js +71 -0
- package/src/commands/op-stubs.js +67 -0
- package/src/commands/preflight.js +6 -2
- package/src/commands/runtime.js +151 -0
- package/src/commands/state-save.js +61 -0
- package/src/commands/sync-agents-preflight.js +117 -3
- package/src/commands/workflow-next.js +64 -0
- package/src/handoff-contract.js +25 -0
- package/src/lib/agent-semantic-diff.js +199 -0
- package/src/operator-memory/conflict.js +202 -0
- package/src/operator-memory/decay.js +157 -0
- package/src/operator-memory/decision.js +274 -0
- package/src/operator-memory/identity.js +109 -0
- package/src/operator-memory/index-md.js +170 -0
- package/src/operator-memory/loader.js +106 -0
- package/src/operator-memory/proposal.js +179 -0
- package/src/operator-memory/prune.js +81 -0
- package/src/operator-memory/slug.js +90 -0
- package/src/operator-memory/storage.js +121 -0
- package/src/preflight-engine.js +91 -1
- package/template/.aioson/agents/dev.md +1 -1
- package/template/.aioson/agents/deyvin.md +3 -3
- package/template/.aioson/agents/neo.md +1 -1
- package/template/.aioson/agents/product.md +1 -1
- package/template/.aioson/agents/setup.md +1 -1
- package/template/.aioson/docs/deyvin/pair-execution.md +1 -1
- package/template/.aioson/skills/process/decision-presentation/SKILL.md +9 -0
- package/template/AGENTS.md +23 -0
- package/template/CLAUDE.md +23 -0
- package/template/agents/_shared/memory-capture-directive.md +115 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,107 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.16.0] - 2026-05-21
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Operator memory — Phase 5 TTL decay + migration + closure** (5 of 5 phases — **feature complete**).
|
|
11
|
+
- **`src/operator-memory/decay.js`** (NEW): per-category half-life engine — identity=365d, autonomy=180d, tooling=90d, default=90d (PMD-03). Env override per category via `AIOSON_OPERATOR_DECAY_<CATEGORY>_DAYS`. `findStaleDecisions(identity)` returns past-half-life entries with 30d per-slug debounce via `~/.aioson/operators/{identity}/_decay_state.json`. `cleanupHistory(identity)` hard-deletes `history/*.md` entries older than 365d.
|
|
12
|
+
- **`src/operator-memory/prune.js`** (NEW): 10k hard cap (PMD-04). `enforceCap(identity, {cap})` prunes oldest non-identity-category decisions first; identity-category decisions are NEVER auto-pruned. Env override via `AIOSON_OPERATOR_MAX_DECISIONS`.
|
|
13
|
+
- **`aioson op:reinforce <slug>`** (NEW Phase 5 command): refreshes `last_reinforced` + increments `reinforcement_count`. Silences decay prompt for next half-life window.
|
|
14
|
+
- **`aioson op:migrate`** (NEW Phase 5 command): explicit one-shot import from `.aioson/context/user-profile.md`. 8 known field mappings (autonomy_preference, communication_style, etc.). Idempotent (checks `deprecated_by: operator-memory` frontmatter). Unknown fields preserved. Marks `user-profile.md` deprecated post-migration per PMD-10.
|
|
15
|
+
- **`aioson op:identity set <id>`** Phase 5 full impl replaces Phase 1 stub: mutates `process.env.AIOSON_OPERATOR_ID` for the current process + initializes the storage tree + returns the shell `export` command for persistence.
|
|
16
|
+
- **`tests/operator-memory-decay.test.js`** — 23 new unit tests AC-P5-01..09 (closure ACs P5-10..14 verified by archive process).
|
|
17
|
+
- **`scripts/smoke-run-chain.js`** `[OM5]` section (decay sweep, hard cap, history cleanup) + `[OM-ALL]` cross-phase loader verification (10 modules + 8 CLI commands all exporting expected functions). Total smoke now 25/25.
|
|
18
|
+
- **`.aioson/context/wiring-audit-operator-memory.md`** Phase 5 entry + **cross-phase consolidation table** (PMD-07 / BR-05 Gate D blocker satisfied). 20 call sites, 114/114 cumulative unit tests, 14 smoke sections green.
|
|
19
|
+
|
|
20
|
+
### Notes
|
|
21
|
+
- **Feature complete.** Operator-memory shipped end-to-end: F1 (storage+identity) + F2 (capture+promotion) + F3 (universal loading directive) + F4 (conflict policy + flag flip) + F5 (decay+migration). 5 phases × 5 minor releases v1.12.0 → v1.16.0 per DD-05 progressive-release strategy (mirrors workflow-handoff-integrity exitoso).
|
|
22
|
+
- **20 call sites, 114 unit tests, 25 smoke checks** wired and exercised across the 5 phases. Cross-phase consolidation table in wiring-audit doc verifies every phase has call sites grepped + tests passing + smoke coverage (PMD-07 / BR-05 anti-pattern guard).
|
|
23
|
+
- Gate D approved post-QA sign-off. `features.md` operator-memory → `done`. Feature artifacts archived to `.aioson/context/done/operator-memory/`.
|
|
24
|
+
- **First operational test in production:** this very release ships AIOSON_OPERATOR_MEMORY default-ON. Existing users on `~/.aioson/` (e.g. me, who has been operating this dev session) will start participating in capture as new sessions begin. The feature is now self-evidence-generating.
|
|
25
|
+
|
|
26
|
+
## [1.15.0] - 2026-05-21
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
- **Operator memory — Phase 4 conflict policy + flag flip** (4 of 5 phases). Binary V1 conflict detection between operator decisions and project rules in `.aioson/rules/`. **Inception flag `AIOSON_OPERATOR_MEMORY` is now default ON** (opt-out via env var).
|
|
30
|
+
- **`src/operator-memory/conflict.js`** (NEW): `detectConflicts`, `debounceConflicts`, `formatConflictWarning`, `scanProjectRules`, `parseRuleFrontmatter`. Keyword-overlap heuristic (≥ 2 stopword-filtered shared keywords) intersected with signal-type filter. Configurable threshold via `AIOSON_OPERATOR_CONFLICT_KEYWORD_THRESHOLD`.
|
|
31
|
+
- **`src/operator-memory/loader.js`** — `preflightLoad` extended with conflict detection when `options.projectRoot` is supplied. Conflicts are debounced per (decision_slug, rule_basename) pair via `_conflict_state.json` (60s default window, mirrors F2 idempotency pattern).
|
|
32
|
+
- **Project rule schema additive**: `.aioson/rules/*.md` may now declare `conflicts_with_signal_types: [authorization, exclusion, correction, confirmation]` in frontmatter. Rules without this field generate zero false positives (backward-compat preserved — AC-P4-04).
|
|
33
|
+
- **`tests/operator-memory-conflict.test.js`** — 18 new unit tests AC-P4-01..10 including **statistical corpus** (10 conflict pairs + 15 non-conflict pairs) with verified FN=0%, FP=0%.
|
|
34
|
+
- **`scripts/smoke-run-chain.js`** `[OM4]` section — 4 smoke checks (binary V1 conflict, additive no-FP, debounce window, flag-flip directive verification).
|
|
35
|
+
- **`template/CLAUDE.md` + `template/AGENTS.md`** universal directive updated: signals "Default **ON** in v1.15.0+. Opt out via `AIOSON_OPERATOR_MEMORY=false`". Byte parity between the two files preserved (T5 + AC-P3-11). New size: 1307 B per file × 2 = 2614 B total (improvement from 2664 B).
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
- **`AIOSON_OPERATOR_MEMORY` default flipped from `false` → `true`** per AC-P4-08. Agents now read `MEMORY.md` at preflight (when present) by default. Existing AIOSON behavior is preserved when no MEMORY.md exists per identity — directive degrades gracefully (AC-P3-08 backward-compat unchanged).
|
|
39
|
+
- Updated wording in directive sections to be flip-aware (still byte-identical between CLAUDE.md and AGENTS.md per parity invariant).
|
|
40
|
+
|
|
41
|
+
### Notes
|
|
42
|
+
- **Operator memory is now active by default.** New users running v1.15.0+ will have their first signal captures land in `~/.aioson/operators/{hash}/proposals/` automatically when agents emit `aioson op:capture`. Promotion at the 2x threshold (PMD-07) continues to be silent on first detection and emits the 1-line audit on promotion.
|
|
43
|
+
- Smoke runner result: 21/21 green (was 17/17 before OM4). The flag-flip safety gate (smoke must be green BOTH flag-off and flag-default) is satisfied — Phase 3's backward-compat tests still pass under default-on mode because the helpers degrade gracefully when no storage exists.
|
|
44
|
+
- Phase 5 (v1.16.0) ships next: per-category TTL decay, 10k hard cap enforcement, `op:reinforce`, `op:migrate` (one-shot import from `user-profile.md`), `op:identity set` full impl, history/ cleanup at 365d, cross-phase wiring audit consolidation, Gate D, and feature:archive. That's the closure release.
|
|
45
|
+
|
|
46
|
+
## [1.14.0] - 2026-05-21
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
- **Operator memory — Phase 3 universal loading directive** (3 of 5 phases for the `operator-memory` feature). The cross-cutting integration phase that wires operator-memory into agent prompts framework-wide. **Inception-risk phase** — directive ships behind `AIOSON_OPERATOR_MEMORY=true` flag **default OFF** until Phase 4 (v1.15.0) ships green.
|
|
50
|
+
- **Universal directive** injected into `template/CLAUDE.md` AND `template/AGENTS.md`: `## Memory loading` + `## Memory capture` sections at consistent position (after `## Mandatory first action`, before `## Agents`). Byte-identical between both files (T5 parity guarantee). Flag-gated: `if process.env.AIOSON_OPERATOR_MEMORY === 'true'` — when unset/false the directive is a no-op (backward-compat per AC-P3-08).
|
|
51
|
+
- **`aioson op:list` full impl** — replaces Phase 1 stub. Lists active decisions with category + signal_type + reinforced date. Supports `--proposals` (queue view), `--include-archived` (Phase 5 archive tier), `--format=json` (machine-readable).
|
|
52
|
+
- **`aioson op:show <slug>` full impl** — replaces Phase 1 stub. Prints decision frontmatter + body, or proposal data when slug is in `proposals/`. `--json` supported.
|
|
53
|
+
- **`src/operator-memory/index-md.js`** (NEW): MEMORY.md tier-based reader/writer. `loadMemoryIndex(identity, tier)` parses frontmatter + link entries. `regenerateIndex(identity)` rebuilds from `decisions/*.md` filesystem (markdown source-of-truth per PMD-AN-06). Hooked into `promoteProposal` + `forgetEntry` post-commit so MEMORY.md auto-refreshes after every state change.
|
|
54
|
+
- **`src/operator-memory/loader.js`** (NEW): `preflightLoad(identity, taskDescription)` returns `{index, matches}` for agent-side consumption. `matchDecisions(index, taskDescription)` V1 substring + stopword heuristic (V2 will switch to FTS5-backed query).
|
|
55
|
+
- **`.aioson/docs/operator-memory/memory-md-format.md`** (NEW): canonical cross-harness format spec. Documents MEMORY.md frontmatter + body schema, decision file schema, loading pseudocode, V1 support matrix (Claude Code native + Codex compatible + Gemini compatible; Cursor + Aider TBD V2), and a ~10-line POSIX reference implementation.
|
|
56
|
+
- **`scripts/memory-budget-audit.js`** (NEW): enforces NFR-02 byte budgets. Per-file warn at 1500B / fail at 2000B; cross-cutting warn at 5000B / fail at 6000B. Phase 3 directive total: 2664 B (well within budget). `--json` supported. Exit 1 on fail.
|
|
57
|
+
- **`tests/operator-memory-loading.test.js`** — 23 new unit tests covering AC-P3-01..12 including byte-parity between CLAUDE.md/AGENTS.md directives + budget audit self-test + cross-harness format spec sanity.
|
|
58
|
+
- **`scripts/smoke-run-chain.js`** extended with `[OM3]` section — 3 smoke checks (index regenerates after promote, lazy match returns task-relevant decisions, flag-OFF graceful degrade).
|
|
59
|
+
|
|
60
|
+
### Notes
|
|
61
|
+
- **Inception risk explicitly mitigated**: `AIOSON_OPERATOR_MEMORY` flag is **OFF by default in this release**. Existing AIOSON sessions are unaffected — directive in template files is a no-op until the env var is set. Phase 4 (v1.15.0) will flip the default to `true` AFTER its CI gate confirms both flag-states are green.
|
|
62
|
+
- The directive is byte-identical between `template/CLAUDE.md` and `template/AGENTS.md` (verified by AC-P3-11 test). This is the parity contract — different file shells, identical directive content.
|
|
63
|
+
- Smoke runner result: 17/17 green (was 14/14 before OM3).
|
|
64
|
+
- MEMORY.md tier-based format (PMD-AN-02): Phase 3 ships single `MEMORY.md` (active tier). Phase 5 decay sweep will partition into `MEMORY.md` + `MEMORY-archive.md` based on category half-life crossing. Format is forward-compatible.
|
|
65
|
+
- Cross-harness V1 support matrix documented: Claude Code + Codex + Gemini CLI all read `CLAUDE.md` or `AGENTS.md`, both of which now contain the universal directive — they participate natively when the env flag is set. Cursor + Aider deferred to V2 (need bridge files).
|
|
66
|
+
|
|
67
|
+
## [1.13.0] - 2026-05-21
|
|
68
|
+
|
|
69
|
+
### Added
|
|
70
|
+
- **Operator memory — Phase 2 capture + promotion engine** (2 of 5 phases for the `operator-memory` feature). Builds on Phase 1's storage substrate to deliver the actual signal-capture pipeline:
|
|
71
|
+
- **`aioson op:capture` full impl** — replaces Phase 1 stub. Captures a standing-decision signal of type `authorization | exclusion | correction | confirmation`, derives deterministic slug from `--proposal`, writes to `proposals/{slug}.md` on first detection (silent), promotes to `decisions/{slug}.md` atomically on second detection (per PMD-07 2x threshold) with the 1-line audit `✔ Memory: '<text>'. aioson op:forget <slug> p/ desfazer.` (PMD-08 silent-by-default with audit-on-promotion).
|
|
72
|
+
- **`aioson op:promote <slug>` full impl** — manual promotion path that skips the 2x threshold for a pending proposal.
|
|
73
|
+
- **`aioson op:forget <slug>` full impl** — soft-deletes a decision or proposal to `history/{ISO}-{slug}.md`. Idempotent (second call returns noop). Removes FTS5 row inside SQLite transaction.
|
|
74
|
+
- **`src/operator-memory/slug.js`** new module: `deriveSlug`, `normalize`, `fingerprintProposal`. Deterministic kebab-case + stopword filter + truncation at word boundary + collision-suffix detection.
|
|
75
|
+
- **`src/operator-memory/proposal.js`** new module: `captureSignal` (write/increment), `readProposal`, `deleteProposal`. Quotes capped at 5 most recent per AC-P2-01.
|
|
76
|
+
- **`src/operator-memory/decision.js`** new module: `promoteProposal` (atomic via SQLite transaction + atomic rename per AC-P2-03), `forgetEntry`, `readDecision`, `inferCategory` (V1 keyword heuristic for autonomy/identity/tooling/default per PMD-03).
|
|
77
|
+
- **`template/agents/_shared/memory-capture-directive.md`** — NEW versioned prompt template (`schema_version: "1.0"`). 4 signal types × ≥3 concrete examples + anti-pattern section + capture-call format. PMD-02 acknowledged divergence from AIOSON's deterministic principle. File is dormant in Phase 2 — Phase 3 wires it into `template/CLAUDE.md`/`AGENTS.md`.
|
|
78
|
+
- **`tests/operator-memory-capture.test.js`** — 26 new unit tests covering AC-P2-01..12 (capture, promote, forget, signal validation, atomicity, FTS5 mirror correctness, category inference, body cap).
|
|
79
|
+
- **`scripts/smoke-run-chain.js`** extended with `[OM2]` section — 3 smoke checks exercising capture+promote pipeline, idempotent forget, signal validation in isolated tmp HOME.
|
|
80
|
+
- **`.aioson/context/wiring-audit-operator-memory.md`** Phase 2 entry populated with call sites, tests, atomicity verification.
|
|
81
|
+
|
|
82
|
+
### Notes
|
|
83
|
+
- Atomicity per AC-P2-03: SQLite `db.transaction()` wraps fs operations (`writeFileSync` to `.tmp` + `renameSync` + `unlinkSync` of proposal). Crash mid-transaction → SQLite rolls back; tmp file cleaned up via `finally`. POSIX `rename(2)` and Windows `MoveFileEx` provide the atomic-rename guarantee.
|
|
84
|
+
- Telemetry events shipped this release: `op_capture`, `op_promote`, `op_forget` via existing `dossierTelemetry.emitDossierEvent` (PMD-12 + DD-04 confirmed: extend, don't fragment).
|
|
85
|
+
- LLM-driven capture is acknowledged divergence from AIOSON's deterministic principle (PMD-02). The prompt template at `template/agents/_shared/memory-capture-directive.md` is the canonical signal-detection spec; versioned `schema_version` field supports V1→V2 migration.
|
|
86
|
+
- Smoke runner result: 14/14 green (was 11/11 before OM2 section).
|
|
87
|
+
- Phase 3 (Universal loading directive, v1.14.0) ships next. **Inception risk:** Phase 3 modifies template files this framework itself uses — flag-gated `AIOSON_OPERATOR_MEMORY=true` default OFF until Phase 4 ships green.
|
|
88
|
+
|
|
89
|
+
## [1.12.0] - 2026-05-21
|
|
90
|
+
|
|
91
|
+
### Added
|
|
92
|
+
- **Operator memory — Phase 1 storage + identity foundation** (1 of 5 phases for the `operator-memory` feature). Establishes the per-operator memory substrate that all subsequent phases build on:
|
|
93
|
+
- **`aioson op:identity`** CLI command — resolves operator identity via sha256[0..16] hash of `git config user.email`, with `AIOSON_OPERATOR_ID` env override (validated regex `^[a-z0-9][a-z0-9-]{2,31}$`, reserved prefixes `_*` and `aioson-*` blocked per PMD-05). Subcommands: `show` (full), `set <id>` (Phase 1 stub — full impl ships Phase 5).
|
|
94
|
+
- **`~/.aioson/operators/` storage tree** auto-created per identity: `decisions/`, `proposals/`, `history/` subdirs (Phase 2+ populates). Hybrid storage backend: shared `_index.sqlite` (FTS5 virtual table + `operators` table) for cross-decision search per PMD-01.
|
|
95
|
+
- **5 CLI command stubs** (`op:capture`, `op:promote`, `op:forget`, `op:list`, `op:show`) — register the command surface, emit `op_command_stub` telemetry on invocation, return exit 1 with structured "Not yet implemented (ships in Phase N / vX.Y.Z)" message. Full impls ship across Phases 2-3.
|
|
96
|
+
- **`src/operator-memory/{identity,storage}.js`** new pure-helper modules exporting `resolveIdentity`, `validateOverride`, `hashEmail`, `ensureStorageTree`, `openIndexDb`, `migrateIndexSchema`, `recordIdentityActivity`. Reusable by downstream phase commands.
|
|
97
|
+
- **`tests/operator-memory-identity.test.js`** — 24 unit tests covering AC-P1-01..10 + EC-08 salt rehash + DD-02 hash size invariant.
|
|
98
|
+
- **`.aioson/context/wiring-audit-operator-memory.md`** — new Gate D blocker doc, Phase 1 entry populated (will accumulate per-phase entries across v1.12.0 → v1.16.0).
|
|
99
|
+
|
|
100
|
+
### Notes
|
|
101
|
+
- This release opens Phase 1 of `operator-memory` MEDIUM feature (5-phase progressive rollout DD-05 mirroring `workflow-handoff-integrity` v1.9.5 → v1.10.0). Subsequent phases ship as v1.13.0 → v1.16.0.
|
|
102
|
+
- Per PMD-02, signal-detection capture (Phase 2+) acknowledges divergence from AIOSON's deterministic principle: prompt-template-driven inherently fuzzy. Phase 1 ships the substrate only — no LLM behavior is invoked.
|
|
103
|
+
- Per inception risk mitigation: universal loading directive (Phase 3) ships behind `AIOSON_OPERATOR_MEMORY=true` flag default OFF; flip default-on after Phase 4 ships green.
|
|
104
|
+
- DD-02 ratified: 16-char hash provides 2^64 collision space; email entropy (~25-30 bits) is the reverse-lookup bottleneck, not hash output length.
|
|
105
|
+
|
|
106
|
+
## [1.11.0] - 2026-05-20
|
|
107
|
+
|
|
7
108
|
### Added
|
|
8
109
|
- **@pentester agent**: adversarial security review agent with structured findings output (`security-findings-{slug}.json`) and Gate D blocking capability for MEDIUM projects.
|
|
9
110
|
- **@discover agent**: system discovery and semantic knowledge cache bootstrap for brownfield projects.
|
|
@@ -33,6 +134,93 @@ All notable changes to this project will be documented in this file.
|
|
|
33
134
|
- Safe canonical English agent sources restored after i18n decoupling.
|
|
34
135
|
- Accidentally tracked local directories removed from git tracking.
|
|
35
136
|
|
|
137
|
+
## [1.10.0] - 2026-05-20
|
|
138
|
+
|
|
139
|
+
### Added
|
|
140
|
+
- **CI pre-publish smoke chain** (Phase 5 / T6 of `workflow-handoff-integrity` — **closes the feature**). New `scripts/smoke-run-chain.js` standalone runner exercises real exported APIs from Phases 1-4 (F1 stale dev-state + state:reset, F2 agent:done auto-advance, F3 workflow:next pending guard, T5 semantic sync parity) plus a final actual-repo parity safety net. 11 deterministic checks; uses isolated `os.tmpdir()` fixtures (DD-04 mock-only, no LLM calls).
|
|
141
|
+
- **`.github/workflows/release-smoke.yml`** GitHub Actions workflow triggered by the `release` PR label or manual `workflow_dispatch`. Runs the full test suite + smoke chain (`AIOSON_PREPUBLISH=true`) + `npm pack --dry-run` as a merge gate before release-labeled PRs can ship to npm.
|
|
142
|
+
- **`tests/scripts/smoke-run-chain.test.js`** — 3 unit tests covering AC-T6-01 (green exit), AC-T6-05 (prepublish mode green on clean repo), AC-T6-08 (output discipline — all 5 sections present).
|
|
143
|
+
- **`tests/fixtures/medium-feature-mock/`** — 6 mock JSON files (one per MEDIUM agent: product, analyst, architect, pm, dev, qa) with `writes` and `spec_frontmatter` templates, plus README documenting PMD-05 / Sheldon R2 fixture-freshness rule.
|
|
144
|
+
|
|
145
|
+
### Notes
|
|
146
|
+
- **Feature closure.** `workflow-handoff-integrity` is now fully implemented across F1 (state hygiene) + F2 (forward auto-emit) + F3 (pending-decisions gate) + T5 (structural drift detection) + T6 (CI smoke). Wiring audit cross-phase consolidation completed — see `.aioson/context/wiring-audit-workflow-handoff-integrity.md`.
|
|
147
|
+
- **DD-05 progressive-release strategy completed:** v1.9.5 → v1.9.6 → v1.9.7 → v1.9.8 → v1.10.0 across 5 minor bumps. Each phase was shippable independently; this final v1.10.0 closes the feature with the cross-phase smoke gate.
|
|
148
|
+
- Smoke runner local result: `pass=11 fail=0 — All smoke checks green. Safe to proceed with publish.`
|
|
149
|
+
|
|
150
|
+
## [1.9.8] - 2026-05-20
|
|
151
|
+
|
|
152
|
+
### Added
|
|
153
|
+
- **Semantic parity check between workspace and template agent files** (Phase 4 / T5 of `workflow-handoff-integrity`). `sync-agents-preflight` now runs three additional diff strategies on top of the existing `## Feature dossier` length check: (1) header diff (`##`/`###` presence + order), (2) section-content hash diff (catches body drift even when headers match — exactly the 981a8fd-style migration gap), (3) frontmatter field-level diff. Each issue includes an actionable hint.
|
|
154
|
+
- **Mode-aware severity** via `AIOSON_PREPUBLISH=true` env var. Default mode (local dev, CI without pre-publish): semantic drift is a warning, non-blocking. Pre-publish mode: warning becomes hard fail — blocks `npm publish` until drift is resolved.
|
|
155
|
+
- **`src/lib/agent-semantic-diff.js`** new pure-helpers module exporting `extractHeaders`, `extractSections`, `extractFrontmatter`, `diffHeaders`, `diffSectionContent`, `diffFrontmatter`, `diffAgentFile`, `normalizeBody`, `hashBody`. Reusable by downstream consumers.
|
|
156
|
+
- **`checkSemanticParity(projectRoot)`** exported from `src/commands/sync-agents-preflight.js`.
|
|
157
|
+
- **`tests/sync-agents-preflight-semantic.test.js`** — 20 unit tests covering AC-T5-01..08 including a **regression guard test** that reproduces the 981a8fd-style diff inside an isolated fixture and confirms the new check catches it.
|
|
158
|
+
|
|
159
|
+
### Changed
|
|
160
|
+
- `src/commands/sync-agents-preflight.js`: `main()` now also runs semantic parity. Existing length check + learning-loop checks kept (additive). Telemetry event `semantic_parity_violation` emitted on detection (per-existing `dossierTelemetry` pattern).
|
|
161
|
+
|
|
162
|
+
### Notes
|
|
163
|
+
- This release closes Phase 4 of `workflow-handoff-integrity`. F1+F2+F3+T5 now cover state hygiene, forward auto-emit, gating against pending decisions, AND structural drift detection between workspace/template. Phase 5 (T6 — CI smoke ponta-a-ponta) ships next as v1.10.0.
|
|
164
|
+
- DD-03 (semantic diff granularity) resolved as: section-level + token-aware code blocks + frontmatter field-level. Plain text body diff deliberately skipped to avoid cosmetic noise (typo fixes).
|
|
165
|
+
- Smoke against actual repo: `checkSemanticParity(process.cwd())` returns 0 drift issues — confirms workspace ↔ template agent files are aligned and v1.9.4 AskUserQuestion mass-edit preserved parity correctly.
|
|
166
|
+
|
|
167
|
+
## [1.9.7] - 2026-05-20
|
|
168
|
+
|
|
169
|
+
### Added
|
|
170
|
+
- **Stale `dev-state.md` detection with actionable warnings** (Phase 3 / F1 of `workflow-handoff-integrity`). `aioson preflight` now cross-references `.aioson/context/features.md` and applies a 30-day TTL: stale conditions are (a) feature already marked `done`/`abandoned`, (b) feature absent from features.md (orphan / cross-project leak), (c) `last_updated > 30 days`. Each warning embeds the command to fix it (`aioson state:reset` or `aioson state:save --feature=<slug>`).
|
|
171
|
+
- **`aioson state:reset`** new CLI command. Removes `.aioson/context/dev-state.md`. `--archive` flag moves to `.aioson/runtime/devstate-history/{ISO}.md` for audit trail. Idempotent. `--json` returns structured result.
|
|
172
|
+
- **Corrupt dev-state detection (AC-F1-08).** `readDevState` flags `parseError` when the file lacks frontmatter markers or has empty frontmatter. `detectStaleDevState` returns a warning with a `state:reset` command suggestion.
|
|
173
|
+
- **`detectStaleDevStateRich` + `parseFeaturesMap`** exported from `src/preflight-engine.js` for downstream consumers + tests.
|
|
174
|
+
- **`tests/preflight-stale-devstate.test.js`** — 20 unit tests covering AC-F1-01..08, parseFeaturesMap robustness, and runStateReset (idempotent, archive variant, json mode).
|
|
175
|
+
|
|
176
|
+
### Changed
|
|
177
|
+
- `src/commands/preflight.js`: `runPreflight` switched the stale-detection call from sync `detectStaleDevState` to async `detectStaleDevStateRich`. Existing sync helper preserved (still used internally by `evaluateReadiness`) for backward-compat.
|
|
178
|
+
|
|
179
|
+
### Fixed
|
|
180
|
+
- Per PRD ("warning acionável, NÃO cleanup automático silencioso"), F1 delivers a structured stderr warning with embedded command suggestion. No interactive y/N prompt (safer for CI/non-TTY contexts than plan-f1 originally implied).
|
|
181
|
+
|
|
182
|
+
### Notes
|
|
183
|
+
- This release closes Phase 3 of `workflow-handoff-integrity`. F1 + F2 + F3 now cover state hygiene (Phase 3 — F1), forward auto-emit (Phase 1 — F2), and gating against pending decisions (Phase 2 — F3). Phases 4-5 (T5 semantic sync, T6 CI smoke) ship as v1.9.8 → v1.10.0.
|
|
184
|
+
- DPC-07 (additional path correction discovered): the PRD/architecture referenced `src/preflight.js` which does not exist. Actual layout: `src/preflight-engine.js` (helpers) + `src/commands/preflight.js` (CLI command). Both extended.
|
|
185
|
+
|
|
186
|
+
## [1.9.6] - 2026-05-20
|
|
187
|
+
|
|
188
|
+
### Added
|
|
189
|
+
- **`aioson workflow:next --complete=<agent>` rejects advance when manifest has pending decisions** (Phase 2 / F3 of `workflow-handoff-integrity`). Reads `.aioson/plans/{slug}/manifest.md` frontmatter; if `status` matches `^pending-(.+)-decisions$`, throws `WORKFLOW_NEXT_PENDING_DECISIONS` with actionable message recommending the agent that resolves those decisions (e.g. `pending-architect-decisions` → "Próximo agente recomendado: @architect"). Prevents the deadlock observed in `aioson-com` 2026-05-19 where `/analyst` routed to `/dev` despite manifest pending.
|
|
190
|
+
- **`--force` flag** on `aioson workflow:next` for explicit override (logs warning, proceeds). For emergency-use cases.
|
|
191
|
+
- **DD-02 hybrid regex+whitelist:** regex `^pending-(.+)-decisions$` catches any future `pending-<X>-decisions` state automatically; whitelist `[architect, product, pm, qa]` flags unrecognized captured groups (still blocks but warns "estado desconhecido" so typos don't silently route to nonexistent agents).
|
|
192
|
+
- **`tests/workflow-next-pending-guard.test.js`** — 10 unit tests covering AC-F3-01..07 (hard error, regex match per known agents, unknown group warning, --force override, no manifest, no slug, pattern specificity, whitelist export).
|
|
193
|
+
|
|
194
|
+
### Changed
|
|
195
|
+
- `src/commands/workflow-next.js`: new public helpers `assertManifestNotPending(targetDir, slug, force)` + `PENDING_STATE_WHITELIST` const exported. Guard fires at start of `options.complete` branch (line 992, BEFORE `finalizeCurrentStage`) per AC-F3-05 precedence.
|
|
196
|
+
|
|
197
|
+
### Notes
|
|
198
|
+
- This release closes Phase 2 of `workflow-handoff-integrity`. F2 + F3 together cover the forward (auto-emit) and gating (pre-check pending) directions of workflow handoff integrity. Phases 3-5 (F1 stale dev-state, T5 semantic sync, T6 CI smoke) ship as v1.9.7 → v1.10.0.
|
|
199
|
+
- Full npm test: 1 transient Windows tempdir flake (L-02 documented) — confirmed transient via targeted re-run of `tests/external-session.test.js` (21/21 pass). All other tests green.
|
|
200
|
+
|
|
201
|
+
## [1.9.5] - 2026-05-20
|
|
202
|
+
|
|
203
|
+
### Added
|
|
204
|
+
- **`agent:done` auto-emits `workflow:next --complete=<agent>`** (Phase 1 / F2 of `workflow-handoff-integrity`). When a workflow is active for the project (`.aioson/runtime/workflow.state.json` present + matching feature) AND the calling agent has produced its canonical artifact on disk, `aioson agent:done` now internally advances the workflow pointer. Removes the requirement for every agent prompt to literal-call `aioson workflow:next` — centralizes the trigger in `runAgentDone`. Backward-compat preserved: state file absent → no auto-advance (baseline stdout byte-identical).
|
|
205
|
+
- **`src/handoff-contract.js#getCanonicalArtifactsForAgent(agent, targetDir, state)`** public helper. Consumes the existing CONTRACTS map; returns absolute artifact paths array, `null` for unknown agents, `[]` for agents with no canonical artifact (e.g. `@committer`, `@dev`).
|
|
206
|
+
- **`--no-auto-advance` opt-out flag** on `aioson agent:done` for cases where auto-emit is undesirable (debug, manual restore, scripts).
|
|
207
|
+
- **`tests/baselines/agent-done-stdout.txt`** — backward-compat baseline lock per Risk-11 mitigation.
|
|
208
|
+
- **`tests/agent-done-auto-emit.test.js`** — 13 unit tests covering AC-F2-01..10 (happy path, backward-compat, opt-out, idempotency 1s window, corrupt state, missing artifact, unknown agent).
|
|
209
|
+
|
|
210
|
+
### Changed
|
|
211
|
+
- `src/commands/runtime.js#runAgentDone` injects `maybeAutoAdvanceWorkflow` call after stdout log in both live-session and standalone branches. Idempotency via `last_workflow_event_at` field added to `workflow.state.json` schema (backward-compat: missing field treated as zero).
|
|
212
|
+
|
|
213
|
+
### Notes
|
|
214
|
+
- This release closes Phase 1 of `workflow-handoff-integrity` MEDIUM feature. Phases 2-5 (F3 CLI guard, F1 stale dev-state, T5 semantic sync, T6 CI smoke) ship as separate releases v1.9.6 → v1.10.0 per progressive release strategy (DD-05).
|
|
215
|
+
- Full npm test: 2520/2521 pass; the single skipped/flaky test is AC-ALL-101 (`telemetry-foundation.test.js`, performance threshold) — pre-existing, documented as separate follow-up.
|
|
216
|
+
- Inception note: this hotfix was implemented via the AIOSON chain itself (`@analyst → @architect → @pm → @dev`) — eating its own dog food.
|
|
217
|
+
|
|
218
|
+
## [1.9.4] - 2026-05-20
|
|
219
|
+
|
|
220
|
+
### Fixed
|
|
221
|
+
- **`AskUserQuestion` no longer fires on bare agent activation.** When `/deyvin`, `/product`, `/dev`, `/neo`, or `/setup` loaded without a stated task, the agents were inventing multi-choice options around fabricated next-steps — wasting user attention and inviting arbitrary implementation paths. New **Rule 7** in `decision-presentation/SKILL.md` mandates an informational summary + wait when no task is stated; the per-agent hard constraint was reworded from "Always use `AskUserQuestion`" to "When a real decision requires user input, use `AskUserQuestion`". `@deyvin` Working kernel and `pair-execution.md` updated to mirror.
|
|
222
|
+
- Affects: `decision-presentation/SKILL.md`, `agents/{deyvin,product,dev,neo,setup}.md`, `docs/deyvin/pair-execution.md` (workspace + template parity preserved).
|
|
223
|
+
|
|
36
224
|
## [1.9.3] - 2026-05-19
|
|
37
225
|
|
|
38
226
|
### Fixed
|
package/README.md
CHANGED
|
@@ -70,12 +70,55 @@ npx @jaimevalasek/aioson install --reconfigure
|
|
|
70
70
|
npx @jaimevalasek/aioson install --no-interactive
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
**Update**
|
|
73
|
+
**Update** the AIOSON files in your project (respects your saved profile):
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
76
|
npx @jaimevalasek/aioson update
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
> Updating an existing project to a new release? See [Upgrading](#upgrading) — `aioson update` alone is not enough if the CLI itself is out of date.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Upgrading
|
|
84
|
+
|
|
85
|
+
Two things can be on different versions: the **CLI you run** (`aioson`) and the **AIOSON files inside your project** (`.aioson/`). Both need to move together.
|
|
86
|
+
|
|
87
|
+
> ⚠ **The #1 reason `aioson update` "doesn't bring in the new version":** `aioson update` copies the templates bundled with the CLI currently on disk. If your global CLI is on an older release, running `aioson update` from a project will copy that older version's files — no matter how many times you run it. You have to upgrade the CLI itself first.
|
|
88
|
+
|
|
89
|
+
**Step 1 — upgrade the CLI.**
|
|
90
|
+
|
|
91
|
+
If you installed globally:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npm install -g @jaimevalasek/aioson@latest
|
|
95
|
+
aioson --version
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
If `aioson --version` still shows the old version, the binary is being shadowed (older Node on PATH, nvm switch, leftover global install). Reinstall cleanly:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm uninstall -g @jaimevalasek/aioson
|
|
102
|
+
npm install -g @jaimevalasek/aioson@latest
|
|
103
|
+
aioson --version
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Prefer not to install globally at all? Use `npx` pinned to `@latest` — it always fetches the latest published version and ignores anything installed globally:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npx @jaimevalasek/aioson@latest <command>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Step 2 — refresh the AIOSON files in each project.**
|
|
113
|
+
|
|
114
|
+
From inside the project directory:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
aioson update
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
This copies the latest agents, skills, and templates into `.aioson/`, respecting your saved profile. Repeat in every project you want to bring up to date.
|
|
121
|
+
|
|
79
122
|
---
|
|
80
123
|
|
|
81
124
|
## How it works
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -177,7 +177,15 @@ const { runClassify } = require('./commands/classify');
|
|
|
177
177
|
const { runSizing } = require('./commands/sizing');
|
|
178
178
|
const { runDetectTestRunner } = require('./commands/detect-test-runner');
|
|
179
179
|
const { runPulseUpdate } = require('./commands/pulse-update');
|
|
180
|
-
const { runStateSave } = require('./commands/state-save');
|
|
180
|
+
const { runStateSave, runStateReset } = require('./commands/state-save');
|
|
181
|
+
const { runOpIdentity } = require('./commands/op-identity');
|
|
182
|
+
const { runOpCapture } = require('./commands/op-capture');
|
|
183
|
+
const { runOpPromote } = require('./commands/op-promote');
|
|
184
|
+
const { runOpForget } = require('./commands/op-forget');
|
|
185
|
+
const { runOpList } = require('./commands/op-list');
|
|
186
|
+
const { runOpShow } = require('./commands/op-show');
|
|
187
|
+
const { runOpReinforce } = require('./commands/op-reinforce');
|
|
188
|
+
const { runOpMigrate } = require('./commands/op-migrate');
|
|
181
189
|
const { runFeatureClose } = require('./commands/feature-close');
|
|
182
190
|
const { runFeatureArchive } = require('./commands/feature-archive');
|
|
183
191
|
const { runDossierInit, runDossierShow, runDossierAddFinding, runDossierAddCodemap, runDossierLinkRule, runDossierCompact } = require('./commands/dossier');
|
|
@@ -583,6 +591,24 @@ const JSON_SUPPORTED_COMMANDS = new Set([
|
|
|
583
591
|
'pulse-update',
|
|
584
592
|
'state:save',
|
|
585
593
|
'state-save',
|
|
594
|
+
'state:reset',
|
|
595
|
+
'state-reset',
|
|
596
|
+
'op:identity',
|
|
597
|
+
'op-identity',
|
|
598
|
+
'op:capture',
|
|
599
|
+
'op-capture',
|
|
600
|
+
'op:promote',
|
|
601
|
+
'op-promote',
|
|
602
|
+
'op:forget',
|
|
603
|
+
'op-forget',
|
|
604
|
+
'op:list',
|
|
605
|
+
'op-list',
|
|
606
|
+
'op:show',
|
|
607
|
+
'op-show',
|
|
608
|
+
'op:reinforce',
|
|
609
|
+
'op-reinforce',
|
|
610
|
+
'op:migrate',
|
|
611
|
+
'op-migrate',
|
|
586
612
|
'dev:state:write',
|
|
587
613
|
'dev-state-write',
|
|
588
614
|
'feature:close',
|
|
@@ -1324,6 +1350,24 @@ async function main() {
|
|
|
1324
1350
|
command === 'dev-state-write'
|
|
1325
1351
|
) {
|
|
1326
1352
|
result = await runStateSave({ args, options, logger: commandLogger });
|
|
1353
|
+
} else if (command === 'state:reset' || command === 'state-reset') {
|
|
1354
|
+
result = await runStateReset({ args, options, logger: commandLogger });
|
|
1355
|
+
} else if (command === 'op:identity' || command === 'op-identity') {
|
|
1356
|
+
result = await runOpIdentity({ args, options, logger: commandLogger });
|
|
1357
|
+
} else if (command === 'op:capture' || command === 'op-capture') {
|
|
1358
|
+
result = await runOpCapture({ args, options, logger: commandLogger });
|
|
1359
|
+
} else if (command === 'op:promote' || command === 'op-promote') {
|
|
1360
|
+
result = await runOpPromote({ args, options, logger: commandLogger });
|
|
1361
|
+
} else if (command === 'op:forget' || command === 'op-forget') {
|
|
1362
|
+
result = await runOpForget({ args, options, logger: commandLogger });
|
|
1363
|
+
} else if (command === 'op:list' || command === 'op-list') {
|
|
1364
|
+
result = await runOpList({ args, options, logger: commandLogger });
|
|
1365
|
+
} else if (command === 'op:show' || command === 'op-show') {
|
|
1366
|
+
result = await runOpShow({ args, options, logger: commandLogger });
|
|
1367
|
+
} else if (command === 'op:reinforce' || command === 'op-reinforce') {
|
|
1368
|
+
result = await runOpReinforce({ args, options, logger: commandLogger });
|
|
1369
|
+
} else if (command === 'op:migrate' || command === 'op-migrate') {
|
|
1370
|
+
result = await runOpMigrate({ args, options, logger: commandLogger });
|
|
1327
1371
|
} else if (command === 'feature:close' || command === 'feature-close') {
|
|
1328
1372
|
result = await runFeatureClose({ args, options, logger: commandLogger });
|
|
1329
1373
|
} else if (command === 'feature:archive' || command === 'feature-archive') {
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* aioson op:capture — record a standing-decision signal (Phase 2, v1.13.0).
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* aioson op:capture --signal=authorization --quote="..." --proposal="..." --source-agent=dev
|
|
8
|
+
*
|
|
9
|
+
* Flow (architecture-operator-memory.md § Phase 2 capture pipeline):
|
|
10
|
+
* 1. Validate --signal in {authorization, exclusion, correction, confirmation}
|
|
11
|
+
* 2. Resolve identity (Phase 1)
|
|
12
|
+
* 3. deriveSlug(--proposal) — deterministic kebab + truncate
|
|
13
|
+
* 4. Check proposals/{slug}.md:
|
|
14
|
+
* - absent: write proposal with detected_count=1, exit 0 silent
|
|
15
|
+
* - present: detected_count++
|
|
16
|
+
* - count >= 2: promoteProposal (atomic) + stdout audit line
|
|
17
|
+
* - count < 2: update proposal, exit 0 silent
|
|
18
|
+
* 5. Telemetry op_capture before file write
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const fs = require('node:fs');
|
|
22
|
+
const path = require('node:path');
|
|
23
|
+
const { resolveIdentity } = require('../operator-memory/identity');
|
|
24
|
+
const { ensureStorageTree, recordIdentityActivity, openIndexDb, getStorageRoot } = require('../operator-memory/storage');
|
|
25
|
+
const { deriveSlug, fingerprintProposal } = require('../operator-memory/slug');
|
|
26
|
+
const { captureSignal, readProposal, VALID_SIGNAL_TYPES } = require('../operator-memory/proposal');
|
|
27
|
+
const { promoteProposal } = require('../operator-memory/decision');
|
|
28
|
+
const { emitDossierEvent } = require('../lib/dossier-telemetry');
|
|
29
|
+
|
|
30
|
+
const PROMOTION_THRESHOLD = 2;
|
|
31
|
+
|
|
32
|
+
function existsCheckFactory(identity) {
|
|
33
|
+
return (slug) => {
|
|
34
|
+
const existing = readProposal(identity, slug);
|
|
35
|
+
if (!existing) return null;
|
|
36
|
+
return existing.proposal_fingerprint || fingerprintProposal(existing.proposal || '');
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function runOpCapture({ args = [], options = {}, logger }) {
|
|
41
|
+
const targetDir = process.cwd();
|
|
42
|
+
const helpRequested = options.help === true || args.includes('--help') || args.includes('-h');
|
|
43
|
+
if (helpRequested) {
|
|
44
|
+
const msg = `op:capture — capture a standing-decision signal into the proposals queue.
|
|
45
|
+
Usage:
|
|
46
|
+
aioson op:capture --signal=<type> --quote=<verbatim> --proposal=<paraphrase> --source-agent=<agent>
|
|
47
|
+
Signal types: ${VALID_SIGNAL_TYPES.join(', ')}
|
|
48
|
+
First detection writes to proposals/{slug}.md. Second detection promotes to decisions/{slug}.md atomically.`;
|
|
49
|
+
if (options.json) return { ok: true, help: true };
|
|
50
|
+
if (logger) logger.log(msg);
|
|
51
|
+
return { ok: true };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const signal = options.signal;
|
|
55
|
+
const quote = options.quote;
|
|
56
|
+
const proposal = options.proposal;
|
|
57
|
+
const sourceAgent = options['source-agent'] || options.sourceAgent || 'unknown';
|
|
58
|
+
|
|
59
|
+
if (!signal || !proposal) {
|
|
60
|
+
const err = `op:capture — required: --signal=<type> --proposal=<paraphrase>. Got signal=${signal}, proposal=${proposal ? 'present' : 'missing'}.`;
|
|
61
|
+
if (options.json) return { ok: false, error: err };
|
|
62
|
+
if (logger && logger.error) logger.error(err);
|
|
63
|
+
return { ok: false, exitCode: 1, error: err };
|
|
64
|
+
}
|
|
65
|
+
if (!VALID_SIGNAL_TYPES.includes(signal)) {
|
|
66
|
+
const err = `op:capture — invalid --signal='${signal}'. Must be one of: ${VALID_SIGNAL_TYPES.join(', ')}.`;
|
|
67
|
+
if (options.json) return { ok: false, error: err };
|
|
68
|
+
if (logger && logger.error) logger.error(err);
|
|
69
|
+
return { ok: false, exitCode: 1, error: err };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const resolved = resolveIdentity();
|
|
73
|
+
ensureStorageTree(resolved.identity);
|
|
74
|
+
const db = openIndexDb();
|
|
75
|
+
try {
|
|
76
|
+
recordIdentityActivity(db, { identity: resolved.identity, source: resolved.source });
|
|
77
|
+
} finally {
|
|
78
|
+
db.close();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const slug = deriveSlug(proposal, existsCheckFactory(resolved.identity));
|
|
82
|
+
|
|
83
|
+
await emitDossierEvent(targetDir, {
|
|
84
|
+
agent: 'op-capture',
|
|
85
|
+
type: 'op_capture',
|
|
86
|
+
summary: `${signal}: ${slug}`,
|
|
87
|
+
meta: { identity_prefix: resolved.identity.slice(0, 8), signal_type: signal, slug, source_agent: sourceAgent }
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
let result;
|
|
91
|
+
try {
|
|
92
|
+
result = captureSignal({
|
|
93
|
+
identity: resolved.identity,
|
|
94
|
+
slug,
|
|
95
|
+
signal_type: signal,
|
|
96
|
+
quote,
|
|
97
|
+
proposal,
|
|
98
|
+
source_agent: sourceAgent
|
|
99
|
+
});
|
|
100
|
+
} catch (err) {
|
|
101
|
+
const errMsg = `op:capture failed: ${err.message}`;
|
|
102
|
+
if (options.json) return { ok: false, error: errMsg };
|
|
103
|
+
if (logger && logger.error) logger.error(errMsg);
|
|
104
|
+
return { ok: false, exitCode: 1, error: errMsg };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const count = result.proposal.detected_count;
|
|
108
|
+
|
|
109
|
+
if (count >= PROMOTION_THRESHOLD) {
|
|
110
|
+
// Promote to decision
|
|
111
|
+
let decision;
|
|
112
|
+
try {
|
|
113
|
+
decision = promoteProposal({ identity: resolved.identity, proposal: result.proposal });
|
|
114
|
+
} catch (err) {
|
|
115
|
+
const errMsg = `op:capture promotion failed: ${err.message}`;
|
|
116
|
+
if (options.json) return { ok: false, error: errMsg };
|
|
117
|
+
if (logger && logger.error) logger.error(errMsg);
|
|
118
|
+
return { ok: false, exitCode: 1, error: errMsg };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
await emitDossierEvent(targetDir, {
|
|
122
|
+
agent: 'op-capture',
|
|
123
|
+
type: 'op_promote',
|
|
124
|
+
summary: `promoted ${slug} (${signal})`,
|
|
125
|
+
meta: { identity_prefix: resolved.identity.slice(0, 8), slug, signal_type: signal, category: decision.category }
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const auditLine = `✔ Memory: '${proposal}'. aioson op:forget ${slug} p/ desfazer.`;
|
|
129
|
+
if (options.json) {
|
|
130
|
+
return { ok: true, promoted: true, slug, identity: resolved.identity, category: decision.category };
|
|
131
|
+
}
|
|
132
|
+
if (logger) logger.log(auditLine);
|
|
133
|
+
return { ok: true, promoted: true, slug };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// First detection (or below threshold) — silent
|
|
137
|
+
if (options.json) {
|
|
138
|
+
return { ok: true, promoted: false, slug, detected_count: count, identity: resolved.identity };
|
|
139
|
+
}
|
|
140
|
+
return { ok: true, promoted: false, slug, detected_count: count };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = {
|
|
144
|
+
runOpCapture,
|
|
145
|
+
PROMOTION_THRESHOLD
|
|
146
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* aioson op:forget <slug> — soft-delete a decision or proposal to history/.
|
|
5
|
+
* Phase 2 (v1.13.0).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
const { resolveIdentity } = require('../operator-memory/identity');
|
|
10
|
+
const { ensureStorageTree } = require('../operator-memory/storage');
|
|
11
|
+
const { forgetEntry } = require('../operator-memory/decision');
|
|
12
|
+
const { emitDossierEvent } = require('../lib/dossier-telemetry');
|
|
13
|
+
|
|
14
|
+
async function runOpForget({ args = [], options = {}, logger }) {
|
|
15
|
+
const targetDir = process.cwd();
|
|
16
|
+
const positional = (args || []).filter((a) => typeof a === 'string' && !a.startsWith('-') && a !== '.');
|
|
17
|
+
const slug = positional[0];
|
|
18
|
+
|
|
19
|
+
if (options.help === true || args.includes('--help') || args.includes('-h')) {
|
|
20
|
+
if (logger) logger.log('op:forget <slug> — soft-delete a decision or proposal to history/. Idempotent.');
|
|
21
|
+
return { ok: true };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!slug) {
|
|
25
|
+
const err = 'op:forget — required argument: <slug>. Usage: aioson op:forget <slug>';
|
|
26
|
+
if (options.json) return { ok: false, error: err };
|
|
27
|
+
if (logger && logger.error) logger.error(err);
|
|
28
|
+
return { ok: false, exitCode: 1, error: err };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const resolved = resolveIdentity();
|
|
32
|
+
ensureStorageTree(resolved.identity);
|
|
33
|
+
const result = forgetEntry(resolved.identity, slug);
|
|
34
|
+
|
|
35
|
+
await emitDossierEvent(targetDir, {
|
|
36
|
+
agent: 'op-forget',
|
|
37
|
+
type: 'op_forget',
|
|
38
|
+
summary: `${result.mode}: ${slug}`,
|
|
39
|
+
meta: { identity_prefix: resolved.identity.slice(0, 8), slug, mode: result.mode }
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (options.json) {
|
|
43
|
+
return { ok: true, mode: result.mode, archived: result.archivedPath };
|
|
44
|
+
}
|
|
45
|
+
if (result.mode === 'noop') {
|
|
46
|
+
if (logger) logger.log(`op:forget — '${slug}' not found (idempotent no-op).`);
|
|
47
|
+
} else {
|
|
48
|
+
const archivedRel = result.archivedPath ? path.basename(result.archivedPath) : null;
|
|
49
|
+
if (logger) logger.log(`op:forget — '${slug}' archived as history/${archivedRel} (${result.mode}).`);
|
|
50
|
+
}
|
|
51
|
+
return { ok: true, mode: result.mode };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { runOpForget };
|