Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
id: AILOG-2026-09-13-001
title: Baton task inheritance through an unambiguous Charter origin
status: review
created: 2026-09-13
agent: codex-cli-v0.154.0
confidence: high
review_required: true
risk_level: medium
work_verb: implement
design_provenance: new
eu_ai_act_risk: not_applicable
nist_genai_risks: [human_ai_config, information_integrity]
iso_42001_clause: []
observability_scope: none
tags: [baton, adopter-feedback, inheritance]
related: [07-ai-audit/decisions/AIDEC-2026-09-13-001-baton-task-parent.md]
---

# AILOG: Baton task inheritance

## Context and actions

Estoa's authorized adopter contribution (Discussion #426, issue #427) found the
ratified task inheritance absent on e83cc064. A synthetic test failed before the
change (3 failed, 5 passed). The fix reads the parent declaration using the existing
originating_spec relation, then feeds the unchanged classifier and router.

Scope: experiment-baton/src/units.rs, tests/task_inheritance.rs, the prototype status
note in the ratification and the current adopter kit. Batch placement documentation
now follows the ratified inline slot (#428); batch harvesting remains pending.
No versions, framework installation, execution policy or model dispatch changed.

## Decision and verification

The linked AIDEC records why multiple parents remain undeclared. The nine new tests
exercise inventory through routing and CLI, all verbs, provenance, filtered output,
missing and ambiguous parents, invalid declarations, out-of-project symlinks,
read-only behavior and mandatory --dry-run. One early test incorrectly expected
an auditor's default tier to be frontier; inspection showed economic and the test
was corrected, without changing routing policy. Final checks: `cargo test -p straymark-baton --locked --offline` passed 93 tests
(84 existing + 9 new), with no failed or ignored tests; zero-test binary/doc harnesses
are not counted. `cargo clippy -p straymark-baton --all-targets --locked --offline
-- -D warnings`, rustfmt on the new test file and `straymark validate` passed.
The latter reports existing warnings; full Charter schema validation in this clean
clone is unavailable because regenerable framework files are intentionally absent.
No second framework installation was created.

## Limits and pending review

This is repeatability of a deterministic implementation, not human calibration.
Risk escalation remains implementer-only and derives from follow-up severity;
Charter risk_level and Bridge findings do not feed that mechanism. The same-author
check here is not an independent audit. No human documentary approval is recorded.

## Follow-ups

Batch inventory/inheritance and placement implementation remain tracked in #428.
Multi-Charter task ownership and direct spec declaration require a defined mapping;
this patch conservatively leaves such tasks undeclared. Track C needs prospective
units and retrospective labels over 2–4 weeks; no concordance or real savings claimed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
id: AILOG-2026-09-17-001
title: Baton task inheritance — maintainer review amendments (#430)
status: accepted
created: 2026-09-17
agent: claude-opus-5-1m
confidence: high
review_required: false
risk_level: medium
eu_ai_act_risk: not_applicable
nist_genai_risks: [human_ai_config, information_integrity]
iso_42001_clause: []
files_modified:
- experiment-baton/src/units.rs
- experiment-baton/src/main.rs
- experiment-baton/tests/task_inheritance.rs
- experiment-baton/06-work-verb-schema-ratification.md
- experiment-baton/07-track-c-adopter-kit.md
- .straymark/07-ai-audit/decisions/AIDEC-2026-09-13-001-baton-task-parent.md
observability_scope: none
tags: [baton, adopter-feedback, inheritance, review]
related:
- 07-ai-audit/agent-logs/AILOG-2026-09-13-001-baton-task-inheritance.md
- 07-ai-audit/decisions/AIDEC-2026-09-13-001-baton-task-parent.md
---

# AILOG: Baton task inheritance — maintainer review amendments (#430)

## Summary

Maintainer review of #430 (Estoa's fix for #427) found the implementation correct but two
behaviours worth changing before merge. Both are amended on the PR branch, on top of the
adopter's commit, so its authorship and its own AILOG stay intact.

## Context

Measured read-only against the three local adopter corpora (git status unchanged in each):
the original rule classified 86 Sentinel tasks (all of `007-usageguard`), 39 in Estoa and
none in LNXDrive. Sentinel shows the review concern directly: its specs 002–005 carry 2–8
Charters each (the charter-chain pattern), so under "exactly one parent" a spec loses its
task classification the moment it gains a second Charter — retroactively, for tasks already
done. Uniqueness never proved task ownership either: one Charter may cover part of `tasks.md`.

## Actions Performed

1. **Agreeing parents inherit.** Every Charter whose `originating_spec` resolves to the
task's sibling `spec.md` is a candidate parent; the task inherits when all candidates
declare the same `(work_verb, design_provenance)`. Any disagreement, or a candidate that
declares nothing, keeps the task `undeclared`. Agreement is on the *declaration*, not on
the class the current classifier derives from it.
2. **Parents read from raw frontmatter.** Candidate parents now come from
`read_frontmatter_yaml`, not the typed parser: a Charter with an out-of-schema field
(e.g. `effort_estimate: XXL`) still counts as a parent — neither hidden nor disabling
inheritance project-wide. Only unreadable YAML (or a non-string `originating_spec`)
leaves the parent inventory incomplete and disables inheritance, as before.
3. **Say why.** `classify` and `route` print a `note:` on stderr naming the Charters that
disabled inheritance (new `units::task_inheritance_blockers`); JSON on stdout is unchanged.
4. Test harness `Drop` no longer unwraps (a panic while unwinding aborts the test binary).
5. AIDEC, ratification §3 note and Track C kit updated to the amended rule.

## Decisions Made

Recorded as an amendment in `AIDEC-2026-09-13-001` (option 2 refined to "all explicit parents
agree"), chosen by the maintainer in this session.

## Impact

- **Functionality**: more tasks inherit when a spec's Charters agree; none inherit through
disagreement. On today's corpora the counts are unchanged (Sentinel's multi-Charter specs
declare no verb; Estoa's specs have one Charter each).
- **Performance**: one raw-frontmatter read per Charter per inventory. N/A in practice.
- **Security / Privacy / Environmental**: N/A. Still read-only; `route` still requires `--dry-run`.

## Verification

- [x] `cargo test -p straymark-baton --locked`: 97 passed (13 in `task_inheritance.rs`,
replacing the "even when they agree" case and adding typed-invalid and CLI-note cases)
- [x] `cargo clippy -p straymark-baton --all-targets --locked -- -D warnings`
- [x] Read-only re-measurement on Sentinel / Estoa / LNXDrive, git status unchanged
- [x] Manual review performed

## Additional Notes

Estoa's local corpus is inflated by a separate, pre-existing inventory bug (Baton walks nested
git worktrees under `.worktrees/`), handled in its own PR, not here.
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
id: AIDEC-2026-09-13-001
title: Conservative explicit parent resolution for Baton tasks
status: accepted
created: 2026-09-13
agent: codex-cli-v0.154.0
confidence: high
review_required: false
risk_level: medium
eu_ai_act_risk: not_applicable
nist_genai_risks: [human_ai_config]
iso_42001_clause: []
tags: [baton, inheritance]
related:
- 07-ai-audit/agent-logs/AILOG-2026-09-13-001-baton-task-inheritance.md
- 07-ai-audit/agent-logs/AILOG-2026-09-17-001-baton-task-inheritance-maintainer-review-amendment.md
---

# AIDEC: Explicit task parent

## Context

Issue #427 requests the inheritance ratified in #332 without adding task declaration
slots. One spec may have multiple Charters; filenames and titles cannot assign a task.

## Alternatives

1. Pick the first/newest/active Charter or infer from titles: more coverage, but
ambiguous ownership could silently recommend a cheaper model.
2. Require exactly one originating_spec relation to the existing sibling spec.md
inside the project: bounded implementation with explicit evidence, but leaves
multi-Charter specs and incomplete inventories undeclared.
3. Design a new task-to-Charter mapping: fuller solution, requires a separate schema
decision and exceeds this first adopter contribution.
4. (Added in maintainer review.) Every Charter whose originating_spec resolves to the
sibling spec.md is a candidate parent; inherit when all candidates declare the same
work_verb and design_provenance. Same evidence as option 2 — neither proves which
tasks a Charter owns — but stable under the charter-chain pattern.

## Decision

Proposed in the PR: option 2. **Amended in maintainer review (2026-09-17): option 4**,
chosen by the maintainer. Option 2 is not monotonic: Sentinel's specs 002–005 carry
2–8 Charters each, so a spec that gains an agreeing Charter (e.g. a polish Charter)
would retroactively unclassify tasks already done. Uniqueness never established
ownership either, so it bought no safety that agreement does not. Agreement is on the
declaration itself, not on the class the current classifier derives: `operate` and
`operate` + `design_provenance: new` do not agree. A candidate declaring nothing means
disagreement.

Candidate parents are read from the raw frontmatter, so a Charter the typed parser
rejects (an out-of-schema field) still counts as a parent. Only an unreadable
frontmatter makes the parent inventory incomplete; inheritance is then disabled for
the project and the CLI names the blocking Charters on stderr. Only work_verb and
design_provenance flow to the child; parent effort_estimate is not a measurement of
task size. Missing or invalid verbs remain unclassifiable in the existing classifier;
no title fallback or task frontmatter.

## Consequences and review

Preserves deterministic and read-only recommendations. Known false negatives remain
visible as undeclared, and a disagreeing Charter still unclassifies a spec's tasks —
correctly, since ownership is then ambiguous. Extending mappings or risk policy is
separate work. Accepted by the maintainer as the implementation rule for #332's task
inheritance; it is not a schema ratification and adds no declaration slot.
23 changes: 18 additions & 5 deletions experiment-baton/06-work-verb-schema-ratification.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,24 @@ frontmatter nuevo en unidades que no lo tienen.
declara el verbo al grano más fino que sí tenga slot (por batch, vía la línea del ledger). No se
fragmenta artificialmente una unidad homogénea.

**Nota de honestidad (estado del prototipo).** Hoy el prototipo **no** implementa la herencia: cosecha
el verbo del charter frontmatter y de las líneas de follow-up, y deja batch/task como `undeclared`
([`src/units.rs`](src/units.rs)). La herencia es una **regla ratificada aquí**, pendiente de
implementar en la graduación al framework (o en un follow-on de Baton). Documentarla ahora evita que
la graduación invente un mecanismo de declaración por-task que esta ratificación descarta.
**Nota de honestidad (estado del prototipo).** La herencia de Task está implementada
por vínculo explícito: son padres los Charters cuyo `originating_spec` resuelve al
`spec.md` hermano de `tasks.md`, dentro del proyecto, y la tarea hereda cuando **todos**
declaran lo mismo (verbo y procedencia). Así, una cadena de Charters sobre la misma spec
que coincide no desclasifica tareas ya hechas. Hereda verbo y procedencia, no el
esfuerzo del Charter. No se añade frontmatter a `tasks.md`.
Si falta el spec o el padre, si los Charters candidatos discrepan (o alguno no declara)
o si el inventario contiene un Charter con frontmatter ilegible, las tareas permanecen
`undeclared`; en el último caso el CLI nombra el Charter en stderr. Un Charter que solo
incumple el esquema tipado sigue contando como padre. No se deduce pertenencia desde
títulos ni desde el orden de archivos. Un verbo inválido conserva el comportamiento
conservador del clasificador. El vínculo `context_spec` no establece parentesco.

Batch, overrides por ledger, vínculos de follow-ups y declaraciones directas en
spec continúan pendientes. El kit Track C y el placement de batches tienen una
discrepancia documentada en #428; esta corrección de tareas no redefine ese contrato.
La herencia es una regla ratificada; estos límites describen su implementación
parcial en [`src/units.rs`](src/units.rs), no una validación humana de las declaraciones.

## 4. Undeclared = estado honesto

Expand Down
4 changes: 2 additions & 2 deletions experiment-baton/07-track-c-adopter-kit.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ descomentar y llenar:
| Unidad | Slot |
|---|---|
| Charter | Frontmatter: `work_verb:` / `design_provenance:` |
| AILOG (batch ledger) | Frontmatter del AILOG |
| Batch (AILOG ledger) | Línea `- **Work verb**:` en la entrada del batch; placement ratificado, aún pendiente en el inventario (#428) |
| Follow-up | Líneas `- **Work verb**:` / `- **Design provenance**:` |
| Task (SpecKit) | Sin slot propio — hereda del charter/spec padre |
| Task (SpecKit) | Sin slot propio — hereda de los Charters cuyo `originating_spec` resuelve al `spec.md` hermano, si todos declaran lo mismo; vínculo ausente o discrepante → `undeclared` |

### Vocabulario y reglas de decisión (resumen de la ratificación)

Expand Down
22 changes: 20 additions & 2 deletions experiment-baton/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! Coherence Bridge can see. The reconciling `coherence` command (finding
//! classes C1–C4) lands in batch B3.

use std::path::PathBuf;
use std::path::{Path, PathBuf};

use clap::{Parser, Subcommand, ValueEnum};
use colored::Colorize;
Expand All @@ -17,7 +17,7 @@ use straymark_baton::signals::signals_for;
use straymark_baton::speckit;
use straymark_baton::telemetry::{build_report, EconomicTelemetry, UnitRouting};
use straymark_baton::tiers::Policy;
use straymark_baton::units::{inventory, Granularity};
use straymark_baton::units::{inventory, task_inheritance_blockers, Granularity};

#[derive(Parser)]
#[command(
Expand Down Expand Up @@ -174,9 +174,26 @@ fn parse_granularity(s: &str) -> anyhow::Result<Option<Granularity>> {
})
}

/// Say why tasks stay undeclared when an unreadable Charter disables task
/// inheritance (#427) — otherwise the only hint is "declare the verb".
fn note_inheritance_blockers(root: &Path, only: Option<Granularity>) {
if only.is_some_and(|g| g != Granularity::Task) {
return;
}
let blockers = task_inheritance_blockers(root);
if !blockers.is_empty() {
eprintln!(
"{} task inheritance disabled — unreadable Charter frontmatter could hide a competing parent: {}",
"note:".yellow(),
blockers.join(", ")
);
}
}

fn classify_cmd(root: PathBuf, out: OutFmt, granularity: &str) -> anyhow::Result<()> {
let only = parse_granularity(granularity)?;
let units = inventory(&root, only);
note_inheritance_blockers(&root, only);

#[derive(serde::Serialize)]
struct Row {
Expand Down Expand Up @@ -243,6 +260,7 @@ fn route_cmd(
}
let only = parse_granularity(granularity)?;
let units = inventory(&root, only);
note_inheritance_blockers(&root, only);
let policy = Policy::load(&root, config.as_deref());
if policy.using_defaults {
eprintln!(
Expand Down
Loading